UEFA国家比率计算

图片

足球中有两种流行的锦标赛:冠军联赛和欧罗巴联赛。 根据他们的结果,计算出所谓的足球协会等级 。 根据此评分,进一步确定每个国家有多少支球队参加随后的比赛。

在本文中,我将基于开放和免费的lsFusion平台创建一个应用程序,该应用程序将计算此评级。 它将所有数据存储在PostgreSQL中,提供一个用于通过过滤和排序功能更改和显示它们的Web界面,以及使用特殊API导入匹配结果。

用于实现此应用程序的所有代码将包含约300条有效行。

域逻辑


任何信息系统的创建都始于域逻辑的任务。
首先,从逻辑上说出只有代码和名称的最简单目录:

  • 比赛 欧洲冠军联赛或欧罗巴联赛。
  • 季节 。 2018-2019 / 2017-2018等
  • 圆的 。 决赛,半决赛,小组赛等 它可以看作是锦标赛的组成,但在此实现中,我将其作为单独的实体进行了选择。
  • 国别 此应用程序用作足球协会。 例如,摩纳哥的俱乐部位于摩纳哥的国家,但参加法国锦标赛。
  • 会所 巴塞罗那,皇马,曼联等

由于lsFusion使用相同类型的逻辑进行声明,因此我们将声明一个元代码(或代码模板),该元代码将生成相应的逻辑:

目录声明元代码
META defineMasterObject(object, caption, captions, nameLength)
CLASS ### object caption;

id '' = DATA INTEGER ( ### object);
object ( INTEGER id) = GROUP AGGR ### object o BY id(o); //

name '' = DATA ISTRING ( ### object) CHARWIDTH nameLength;
object ( ISTRING name) = GROUP AGGR ### object o BY name(o); //

FORM object caption
OBJECTS o = ### object PANEL
PROPERTIES (o) id, name

EDIT ### object OBJECT o
;

FORM object ## s captions
OBJECTS o = ### object
PROPERTIES (o) READONLY id, name
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

FORM dialog ### object ## s captions
OBJECTS o = ### object
PROPERTIES (o) READONLY id, name

LIST ### object OBJECT o
;

NAVIGATOR {
NEW object ## s;
}
END


他将宣布:

  • 具有给定名称的类
  • 带有代码和名称的新类的属性
  • 三种形式:编辑一个对象,一个包含所有对象列表的表单,然后将其添加到导航器中,一个用于选择该对象的对话框。 作为对话,您可以使用第二种形式,但是用户在选择时将有机会更改对象,这可能会导致用户方面的错误。

四个参数传递给元代码:

  • 标识符(对象) 。 使用此名称,将创建类和表单。 ###构造用于在结果代码中将标识符的首字母大写。
  • 名称以单数表示 。 用于类和表单的标题。
  • 名称以复数形式表示 。 用于组合框和对话框。
  • 名称长度 。 在不同对象的名称中,期望使用不同的长度,这在构建接口时很重要。

使用创建的元代码,添加上述五个实体:
@defineMasterObject (tournament, '' , '' , 20 );
@defineMasterObject (season, '' , '' , 5 );
@defineMasterObject (round, '' , '' , 15 );
@defineMasterObject (country, '' , '' , 10 );
@defineMasterObject (team, '' , '' , 20 );


例如,为锦标赛生成的代码将如下所示:
CLASS Tournament '' ;

id '' = DATA INTEGER (Tournament);
tournament ( INTEGER id) = GROUP AGGR Tournament o BY id(o);

name '' = DATA ISTRING (Tournament) CHARWIDTH 20 ;
tournament ( ISTRING name) = GROUP AGGR Tournament o BY name(o);

FORM tournament ''
OBJECTS o = Tournament PANEL
PROPERTIES (o) id, name

EDIT Tournament OBJECT o
;

FORM tournaments ''
OBJECTS o = Tournament
PROPERTIES (o) READONLY id, name
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

FORM dialogTournaments ''
OBJECTS o = Tournament
PROPERTIES (o) READONLY id, name

LIST Tournament OBJECT o
;

NAVIGATOR {
NEW tournaments;
}

将国家链接添加到生成的俱乐部逻辑中。 为此,首先创建相应的属性,然后将其放在俱乐部的编辑和查看表单中:
country = DATA Country (Team);
nameCountry '' (Team t) = name(country(t));

EXTEND FORM team PROPERTIES (o) nameCountry;
EXTEND FORM teams PROPERTIES (o) READONLY nameCountry;

我们将所有创建的逻辑放在单独的Master模块(文件Master.lsf)中。

现在创建一个实体联赛 。 她将确定特定季节的比赛。 例如,欧洲冠军联赛2017-18或欧洲冠军联赛2018-19。 联赛没有名称,而只是指向比赛和赛季的链接。 因此,我们将不使用以前的元代码,但将执行相同的逻辑并将其放入新的League模块中:
MODULE League;

REQUIRE Master;

CLASS League '' ;

id '' = DATA INTEGER (League);
league ( INTEGER id) = GROUP AGGR League o BY id(o);

tournament = DATA Tournament (League);
nameTournament '' (League l)= name(tournament(l));

season = DATA Season(League);
nameSeason '' (League l)= name(season(l));

FORM league ''
OBJECTS o = League PANEL
PROPERTIES (o) id, nameTournament, nameSeason

EDIT League OBJECT o
;

FORM leagues ''
OBJECTS o = League
PROPERTIES (o) READONLY id, nameTournament, nameSeason
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

FORM dialogLeagues ''
OBJECTS o = League
PROPERTIES (o) READONLY id, nameTournament, nameSeason

LIST League OBJECT o
;

NAVIGATOR {
NEW leagues;
}

最后,添加匹配逻辑。 为此,创建Match类,该类将参考联赛和回合。 对于他来说,参加它的俱乐部以及结果也将被询问。 我们将所有这些放在单独的Match模块中:
MODULE Match;

REQUIRE League;

CLASS Match '' ;

id '' = DATA INTEGER (Match);
match ( INTEGER id) = GROUP AGGR Match o BY id(o);

dateTime '' = DATA DATETIME (Match);

league = DATA League (Match);

tournament (Match m) = tournament(league(m));
nameTournament '' (Match m) = name(tournament(m));

season(Match m) = season(league(m));
nameSeason '' (Match m) = name(season(m));

round = DATA Round (Match);
nameRound '' (Match m) = name(round(m));

homeTeam = DATA Team (Match);
nameHomeTeam '' (Match m) = name(homeTeam(m));

awayTeam = DATA Team (Match);
nameAwayTeam '' (Match m) = name(awayTeam(m));

goalsHome ' ()' = DATA INTEGER (Match);
goalsAway ' ()' = DATA INTEGER (Match);

FORM match ''
OBJECTS o = Match PANEL
PROPERTIES (o) id, dateTime, nameTournament, nameSeason, nameRound,
nameHomeTeam, goalsHome, goalsAway, nameAwayTeam

EDIT Match OBJECT o
;

FORM matches ''
OBJECTS o = Match
PROPERTIES (o) READONLY id, dateTime, nameTournament, nameSeason, nameRound,
nameHomeTeam, goalsHome, goalsAway, nameAwayTeam
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE

LIST Match OBJECT o
;

NAVIGATOR {
NEW matches;
}


资料汇入


不幸的是,我设法找到了一个支持所有Eurocups的公共和免费API。 这是Football API 。 但是,存在一些问题:

  • 直到2016年都没有结果。
  • 直到2018年才有参加欧洲联赛的资格。
  • 数据中存在某些错误。 例如,额尔齐斯·帕夫洛达尔(Ertysh Pavlodar)被分配到俄罗斯,尽管该俱乐部代表哈萨克斯坦。 另外,欧罗巴足球俱乐部出于某种原因是指西班牙,而不是直布罗陀。


可以使用以前创建的表格手动纠正数据错误。 但是,由于总系数的计算是基于最近五年,因此,根据API Football数据来计算总系数将不起作用。 如果评论中有人建议从前几年以任何格式获取必要的数据,我将不胜感激。 但是,由于有完整的2018年数据,因此至少可以在今年验证计算的正确性。

我们需要的API以HTTP请求的形式实现,其中参数是通过url传输的,并且标头中指示了特殊的访问密钥。 声明相应的逻辑:
host = 'api-football-v1.p.rapidapi.com' ;
key ' API Football' = DATA STRING () CHARWIDTH 50 ;

url = 'https://' + host() + '/v2' ;

headers( TEXT name) = CASE
WHEN name = 'x-rapidapi-host' THEN host()
WHEN name = 'x-rapidapi-key' THEN key();

所有数据导入操作将放置在先前创建的“ 联赛”表单上。 在那里,我们将把访问密钥与联赛列表一起放在表格的工具栏中:
EXTEND FORM leagues
PROPERTIES () key DRAW o TOOLBAR
;


首先,我们执行联赛列表。 为此,Football API具有一个特殊的网址:/ Leagues。 对其的GET请求将返回以下形式的JSON:

答案
 { "api":{ "results":2, "leagues":[ { "league_id":1, "name":"2018 Russia World Cup", "country":"World", "country_code":null, "season":2018, "season_start":"2018-06-14", "season_end":"2018-07-15", "logo":"https://www.api-football.com/public/leagues/1.png", "flag":null, "standings":0, "is_current":1 }, { "league_id":2, "name":"Premier League", "country":"England", "country_code":"GB", "season":2018, "season_start":"2018-08-10", "season_end":"2019-05-12", "logo":"https://www.api-football.com/public/leagues/2.png", "flag":"https://www.api-football.com/public/flags/gb.svg", "standings":1, "is_current":1 } ] } } 


要向其生成GET请求并记录响应正文,请使用以下构造:
LOCAL result = FILE ();
EXTERNAL HTTP GET url() + '/leagues' HEADERS headers TO result;

它将结果写入不带FILE类型参数的本地结果属性。

为了解析JSON格式的文件,将构建一个表单,该表单的结构与JSON结构相对应。 您可以使用菜单项在IDE中生成它:

图片

对于上述JSON,该表单将如下所示(仅考虑将要导入的那些值):
GROUP api;

tournamentName = DATA LOCAL STRING ( INTEGER );
seasonName = DATA LOCAL STRING ( INTEGER );
leagueId = DATA LOCAL INTEGER ( INTEGER );

FORM importLeagues
OBJECTS leagues = INTEGER IN api
PROPERTIES (leagues) name = tournamentName, season = seasonName, league_id = leagueId
;

要以importLeagues表单的格式直接从JSON 结果属性导入,请使用以下命令:
IMPORT importLeagues JSON FROM result();

执行后,JSON文件中的相应值将放置在TournamentNameseasonNameLeagueId属性中:

图片

就是说, TournamentName(0)的值将是“世界杯”,而在TournamentName(1)中的值将是“总理联赛”。

不幸的是,API Football根本没有比赛实体。 连接所有联赛的唯一方法是使用与不同季节的同一联赛的联赛相匹配的名称。 为此,在导入中,首先我们将导入的联赛的所有名称分组,如果不在数据库中,则创建新的锦标赛:
FOR [ GROUP SUM 1 BY tournamentName( INTEGER i)]( STRING tn) AND NOT tournament(tn) DO NEW t = Tournament {
name(t) <- tn;
}

也没有季节代码,因此在导入联赛时,它们的创建方式相同。 创建缺少的对象后,将联赛直接导入。 使用先前通过GROUP AGGR构建的属性按名称搜索比赛和季节:
FOR leagueId( INTEGER i) AND NOT league(leagueId(i)) DO NEW l = League {
id(l) <- leagueId(i);
tournament(l) <- tournament(tournamentName(i));
season(l) <- season(seasonName(i));
}

默认情况下,将加载数据,但是仅当用户单击表单上的“保存”按钮时,数据才会保存到数据库。 如有必要,可以在操作结束时添加APPLY命令,以便立即将其保存到数据库中而不进行预览。

最后,将导入操作添加到联赛列表表单中:
EXTEND FORM leagues
PROPERTIES () importLeagues DRAW o TOOLBAR
;

同样,我们导入俱乐部和比赛。 但是,由于API提供了仅将其导入特定联赛的功能,因此操作必须将联赛作为输入:

导入俱乐部和比赛
//
teamId = DATA LOCAL INTEGER ( INTEGER );
teamName = DATA LOCAL STRING ( INTEGER );
countryName = DATA LOCAL STRING ( INTEGER );

FORM importTeams
OBJECTS teams = INTEGER IN api
PROPERTIES (teams) team_id = teamId, name = teamName, country = countryName
;

importTeams ' ' (League l) {
LOCAL result = FILE ();
EXTERNAL HTTP GET url() + '/teams/league/' + id(l) HEADERS headers TO result;

IMPORT importTeams JSON FROM result();
FOR [ GROUP SUM 1 BY countryName( INTEGER i)]( STRING cn) AND NOT country(cn) DO NEW c = Country {
name(c) <- cn;
}

FOR teamId( INTEGER i) AND NOT team(teamId(i)) DO NEW t = Team {
id(t) <- teamId(i);
name(t) <- teamName(i);
country(t) <- country(countryName(i));
}
}

//

matchId = DATA LOCAL INTEGER ( INTEGER );
dateTime = DATA LOCAL STRING ( INTEGER );
roundName = DATA LOCAL STRING ( INTEGER );

GROUP homeTeam;
homeTeamId = DATA LOCAL INTEGER ( INTEGER );

GROUP awayTeam;
awayTeamId = DATA LOCAL INTEGER ( INTEGER );

goalsHome = DATA LOCAL INTEGER ( INTEGER );
goalsAway = DATA LOCAL INTEGER ( INTEGER );

FORM importMatches
OBJECTS fixtures = INTEGER IN api
PROPERTIES (fixtures) fixture_id = matchId, league_id = leagueId, event_date = dateTime, round = roundName,
homeTeamId IN homeTeam EXTID 'team_id' ,
awayTeamId IN awayTeam EXTID 'team_id' ,
goalsHomeTeam = goalsHome, goalsAwayTeam = goalsAway
;

importMatches ' ' (League l) {
LOCAL result = FILE ();
EXTERNAL HTTP GET url() + '/fixtures/league/' + id(l) HEADERS headers TO result;

IMPORT importMatches JSON FROM result();
FOR [ GROUP SUM 1 BY awayTeamId( INTEGER i)]( INTEGER id) AND NOT team(id) DO {
MESSAGE ' ' + id;
RETURN ;
}

FOR [ GROUP SUM 1 BY awayTeamId( INTEGER i)]( INTEGER id) AND NOT team(id) DO {
MESSAGE ' ' + id;
RETURN ;
}

FOR [ GROUP SUM 1 BY roundName( INTEGER i)]( STRING rn) AND NOT round(rn) DO NEW r = Round {
name(r) <- rn;
}

FOR matchId( INTEGER i) AND NOT match(matchId(i)) DO NEW m = Match {
id(m) <- matchId(i);
dateTime(m) <- toDateTimeFormat(left(dateTime(i), 19 ), 'yyyy-MM-ddThh24:mi:ss' );
league(m) <- league(leagueId(i));
round(m) <- round(roundName(i));
homeTeam(m) <- team(homeTeamId(i));
awayTeam(m) <- team(awayTeamId(i));
goalsHome(m) <- goalsHome(i);
goalsAway(m) <- goalsAway(i);
}
}

比赛有一个特殊之处:团队代码位于其他homeTeamawayTeam标签内 。 通过与api相似的方式为它们创建相应的组。 而且,它们内部具有相同的team_id标记。 由于不能将具有相同名称的属性添加到表单,因此使用特殊的EXTID关键字,该关键字在导入的JSON中定义标签名称。

为了使所有进口商品具有相同的形式,并且由于它们与联盟绑定,我们将它们全部取为相同的形式。 另外,我们将团队和比赛添加到表单中,以便能够在保存之前查看导入的内容:
EXTEND FORM leagues
OBJECTS t = Team
PROPERTIES (t) READONLY id, name

PROPERTIES importTeams(o) DRAW t TOOLBAR

OBJECTS m = Match
PROPERTIES (m) READONLY id, dateTime, nameRound, nameHomeTeam, goalsHome, goalsAway, nameAwayTeam
FILTERS league(m) = o

PROPERTIES importMatches(o) DRAW m TOOLBAR
;

DESIGN leagues {
OBJECTS {
NEW leagueDetails {
fill = 2 ;
type = SPLITH ;
MOVE BOX (t);
MOVE BOX (m);
}
}
}


生成的表单将如下所示:
图片

所有导入都将放在单独的APIFootball模块中。

系数计算


我们直接进行UEFA国家系数的计算。 为此,将所有代码放入专门安装的UEFA模块中是合乎逻辑的。

首先,请记住,Football API提供了用于导入所有比赛(而不仅仅是Eurocups)的接口。 因此,我们根据比赛的名称将Eurocup比赛分开(为此,最好有一个单独的主要属性,但是可以随时更改属性的实现而无需修改其余逻辑):
isCL (Tournament t) = name(t) = 'Champions League' ;
isEL (Tournament t) = name(t) = 'Europa League' ;

isUL (Tournament t) = isCL(t) OR isEL(t);
isUL (Match m) = isUL(tournament(m));

首先,让我们为特定比赛的结果计算每个俱乐部在一个赛季中获得的积分。
在此期间,每个团队都会收到:
获胜时得2分;
如果打成平局,则得1分。
自1999年以来,如果在资格赛中获得这些积分,它们将分为两部分,即:
如果获胜,则得1分;
并列得分0.5分。


创建用于确定比赛和球杆之间关系的辅助属性:
played (Team t, Match m) = homeTeam(m) = t OR awayTeam(m) = t;
won (Team t, Match m) = (homeTeam(m) = t AND goalsHome(m) > goalsAway(m)) OR (awayTeam(m) = t AND goalsHome(m) < goalsAway(m));
draw (Team t, Match m) = played(t, m) AND goalsHome(m) = goalsAway(m);

为了确定每场比赛得分多少,我们为该回合添加数字类型的主要属性,默认情况下,该属性等于1:
dataMatchCoeff = DATA NUMERIC [ 10 , 1 ] (Round);
matchCoeff ' ' (Round r) = OVERRIDE dataMatchCoeff(r), 1.0 ;

然后,我们计算出胜利和平局的分数,然后加在一起:
wonPoints ' ' (Season s, Team t) =
GROUP SUM 2 * matchCoeff(round(Match m)) IF won(t, m) AND season(m) = s AND isUL(m);
drawPoints ' ' (Season s, Team t) =
GROUP SUM 1 * matchCoeff(round(Match m)) IF draw(t, m) AND season(m) = s AND isUL(m);
matchPoints ' ' (Season s, Team t) = wonPoints(s, t) (+) drawPoints(s, t) MATERIALIZED ;

匹配点标记为MATERIALIZED,以便将它们保存在表中,而不是每次都计算。

现在您需要计算奖励积分:
此外,还会奖励积分:
如果球队在欧洲杯中闯入四分之一决赛,半决赛和决赛,则获得1分;
进入欧冠小组赛阶段获得4分(直到1996年-2分,从1997年至2003年-1分,从2004年至2008年-3分);
如果一支球队离开欧洲冠军联赛1/8决赛则获得5分(2008年之前为1分)。
仅考虑参加比赛的比赛(不考虑技术损失)。 在计算系数时,会根据结果考虑以一系列点球大战结束的比赛,该结果由主要时间和额外时间的游戏结果决定。

在此实施方式中,我们假设俱乐部参加了至少一场比赛,就进入了比赛的这一轮。 为此,我们计算俱乐部在特定赛季(锦标赛)的一轮中进行了多少场比赛:
played '' (Season s, Tournament t, Round r, Team tm) =
GROUP SUM 1 IF played(tm, Match m) AND round(m) = r AND tournament(m) = t AND season(m) = s;


现在,您需要确定在特定回合中要通过多少得分。 由于这取决于锦标赛(例如,冠军联赛中的段落获得5分,而欧洲联赛中则没有获得分数)。 为此,我们引入主要属性:
bonusPoints ' ' = DATA NUMERIC [ 10 , 1 ] (Tournament, Round);

现在,让我们计算该赛季俱乐部的奖励积分和积分总数:
bonusPoints ' ' (Season s, Team tm) = GROUP SUM bonusPoints(Tournament t, Round r) IF played(s, t, r, tm) MATERIALIZED ;

points '' (Season s, Team tm) = matchPoints(s, tm) (+) bonusPoints(s, tm);

最后,我们直接转到国家系数。
为了计算协会的等级,将参加冠军联赛和欧罗巴联赛的俱乐部的所有得分相加,然后将结果除以该协会的俱乐部数量[2] [3]。

让我们计算出参加欧洲比赛的每个协会的俱乐部数量:
matchesUL ' ' (Season s, Team t) = GROUP SUM 1 IF played(t, Match m) AND season(m) = s AND isUL(m);
teams '' (Season s, Country c) = GROUP SUM 1 IF matchesUL(s, Team t) AND country(t) = c;

现在,我们考虑该赛季的关联点总数,然后除以俱乐部数:
totalPoints ' ()' (Season s, Country c) = GROUP SUM points(s, Team t) IF country(t) = c;
points '' (Season s, Country c) = trunc( NUMERIC [ 13 , 4 ](totalPoints(s, c)) / teams(s, c), 3 );


一个国家的评级是该国家过去5年的系数之和。


为此,我们用内部代码对从最后一个开始的所有季节进行编号(我们假设后者是在以后添加的,并且具有较大的代码):
index '' (Season s) = PARTITION SUM 1 IF s IS Season ORDER DESC s;

如有必要,可以按名称输入单独的字段或数字。
它仅用于计算该国家/地区的最终评分:
rating '' (Country c) = GROUP SUM points(Season s, c) IF index(s) <= 5 ;

以上,我们宣布了锦标赛和回合的赔率。 将它们添加到锦标赛编辑表单中,同时仅过滤这些锦标赛中的回合:
matches (Tournament t, Round r) = GROUP SUM 1 IF tournament(Match m) = t AND round(m) = r;

EXTEND FORM tournament
OBJECTS r = Round
PROPERTIES name(r) READONLY , matchCoeff(r), bonusPoints(o, r)
FILTERS matches(o, r)
;


例如,对于欧洲冠军联赛的赔率设置,您需要这样设置:
图片

让我们绘制一个显示等级的表格,其中将显示每个国家/地区的球队以及每个球队的比赛:
FORM countryCoefficientUEFA ' UEFA'
OBJECTS s = Season
FILTERS index(s) <= 5

OBJECTS c = Country
PROPERTIES (c) READONLY name, rating
PROPERTIES (s, c) COLUMNS (s) points HEADER ' : ' + name(s), teams HEADER ' : ' + name(s)

OBJECTS t = Team
PROPERTIES (t) READONLY nameCountry, name
PROPERTIES (s,t) COLUMNS (s) HEADER name(s) points BACKGROUND matchesUL(s, t)
FILTERGROUP country
FILTER ' ' country(t) = c DEFAULT

OBJECTS m = Match
PROPERTIES (m) READONLY dateTime, nameTournament, nameSeason, nameRound,
nameHomeTeam,
goalsHome BACKGROUND goalsHome(m) > goalsAway(m),
goalsAway BACKGROUND goalsHome(m) < goalsAway(m),
nameAwayTeam
FILTERS played(t, m)
ORDER dateTime(m) DESC
;

DESIGN countryCoefficientUEFA {
OBJECTS {
NEW countryDetails {
type = SPLITH ;
fill = 0.5 ;
MOVE BOX (t);
MOVE BOX (m);
}
}

}

NAVIGATOR {
NEW countryCoefficientUEFA;
}

生成的表单将如下所示:
图片
俱乐部表中的颜色显示了他参加赛季时和比赛表中的颜色-谁赢得了比赛。
图为2018年的评分与Wikipedia上的评分完全相同。 如上所述,对于前几年,Football API并未提供所有信息。

总结



我们构建了一个上面的代码充分描述的小型应用程序,并将其数据存储在PostgreSQL中,提供了用于查看和编辑数据的Web界面。 同时,由于所有表单仅读取可见窗口,因此它将在大批量上有效地工作。 开箱即用的还有过滤器,排序,上载到Excel等。

应该注意的是,使用平台将计算系数的任务分解为各个属性的过程是多么容易。 在执行时,所有这些逻辑都将转换为SQL查询,并且所有计算将直接使用所有DBMS优化在数据库服务器上执行。

有关应用程序如何处理加载到其中的数据的示例,请参见: https : //demo.lsfusion.org/euroleague 。 没有密码的访客登录。 用户处于只读模式。

那些希望的人可以在本地设置所有内容,例如,通过输入未来比赛的结果来对系数建模。 上述所有应用程序模块都托管在github上 。 自动安装后,您只需要从说明中将这些文件弹出到适当的文件夹中,然后重新启动服务器即可。

为了从Football API下载数据,您需要向它们注册并获取API密钥。 它需要一张卡,但是如果您每天进行的请求不超过50个,则不会从中扣除任何费用。

此外,您可以在网站上的相应部分在线运行此应用程序。 在“平台”选项卡上,选择“ UEFA赔率计算”示例,然后单击“播放”。

顺便说一句,如果有人需要实现一些不再适合使用Excel的简单系统,请在注释中编写。 为了学习平台的功能,我们将尝试实现它并编写相应的文章。

Source: https://habr.com/ru/post/zh-CN467279/


All Articles