Cálculo da proporção de países da UEFA

imagem

Existem dois torneios populares no futebol: a Liga dos Campeões e a Liga Europa. Com base nos resultados, é calculado o chamado Rating of Football Associates . Com base nessa classificação, é ainda determinado quantas equipes de cada país participarão dos torneios subsequentes.

Neste artigo, vou criar um aplicativo baseado na plataforma lsFusion aberta e gratuita que calculará essa classificação. Ele armazenará todos os seus dados no PostgreSQL, fornecerá uma interface da web para alterá-los e exibi-los com recursos de filtragem e classificação, além de importar resultados de correspondências usando uma API especial.

Todo o código para implementar este aplicativo consistirá em cerca de 300 linhas significativas.

Lógica do domínio


A criação de qualquer sistema de informação começa com a tarefa da lógica do domínio.
Antes de tudo, é lógico destacar os diretórios mais simples que possuem apenas um código e um nome:

  • Torneio Liga dos Campeões ou Liga Europa.
  • Temporada . 2018-2019 / 2017-2018 etc.
  • Rodada . Final, Semifinal, Fase de Grupos, etc. Pode ser considerado uma composição para o torneio, mas nesta implementação eu o destaquei como uma entidade separada.
  • País de origem Esta aplicação é usada como a Associação de Futebol. Por exemplo, o clube de Mônaco está localizado no país de Mônaco, mas joga no campeonato francês.
  • Club Barcelona, ​​Real Madrid, Manchester United, etc.

Como o lsFusion usa o mesmo tipo de lógica para declará-los, declararemos um metacódigo (ou modelo de código) que gerará a lógica correspondente:

Metacódigo da Declaração de Diretório
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


Ele anunciará:

  • Classe com nome próprio
  • Propriedades com código e nome para a nova classe
  • Três formulários: edição de um objeto, um formulário com uma lista de todos os objetos, que são adicionados ao navegador, uma caixa de diálogo para selecionar esse objeto. Como um diálogo, você pode usar o segundo formulário, mas o usuário terá a oportunidade de alterar os objetos ao escolher, o que pode levar a erros por parte dos usuários.

Quatro parâmetros são passados ​​para o metacódigo:

  • Identificador (objeto) . Com esse nome, classes e formulários serão criados. A construção ### é usada para colocar a primeira letra do identificador em maiúscula no código resultante.
  • Nome no singular . Usado para o título da classe e do formulário.
  • O nome está no plural . Usado para caixa de combinação e caixa de diálogo.
  • Nome Comprimento . Nos nomes de objetos diferentes, são esperados comprimentos diferentes, o que é importante ao criar a interface.

Usando o metacódigo criado, adicione as cinco entidades descritas acima:
@defineMasterObject (tournament, '' , '' , 20 );
@defineMasterObject (season, '' , '' , 5 );
@defineMasterObject (round, '' , '' , 15 );
@defineMasterObject (country, '' , '' , 10 );
@defineMasterObject (team, '' , '' , 20 );


O código gerado, por exemplo, para um torneio terá a seguinte aparência:
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;
}

Adicione o link ao país à lógica do clube gerada. Para fazer isso, primeiro crie a propriedade correspondente, que depois será colocada nas formas de edição e exibição do clube:
country = DATA Country (Team);
nameCountry '' (Team t) = name(country(t));

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

Colocamos toda a lógica criada em um módulo Master separado (arquivo Master.lsf).

Agora crie uma entidade League . Ela determinará o torneio de uma temporada específica. Por exemplo, Liga dos Campeões 2017-18 ou Liga Europa 2018-19. A liga não terá nome, mas apenas links para o torneio e a temporada. Portanto, não usaremos o metacódigo anterior, mas faremos a mesma lógica e a colocaremos no novo módulo 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;
}

E, finalmente, adicione a lógica das correspondências. Para fazer isso, crie a classe Match , que se refere à liga e à rodada. Para ele, os clubes que participaram e o resultado também serão solicitados. Colocamos tudo isso em um módulo Match separado:
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;
}


Importação de dados


Infelizmente, consegui encontrar apenas uma API pública e gratuita que suporta todos os Eurocups. Esta é a API do futebol . No entanto, existem problemas:

  • Não há resultados até 2016.
  • Não há qualificações para a Liga Europa até 2018.
  • Existem certos erros nos dados. Por exemplo, Irtysh Pavlodar está designado para a Rússia, embora este clube represente o Cazaquistão. Além disso, Europa Fc, por algum motivo, refere-se à Espanha, em vez de Gibraltar.


Os erros de dados podem ser corrigidos manualmente usando formulários criados anteriormente. No entanto, como o cálculo do coeficiente total é baseado nos últimos cinco anos, infelizmente, não funcionará para calcular o coeficiente total a partir dos dados da API Football. Se alguém nos comentários sugerir onde obter os dados necessários em qualquer formato dos anos anteriores, ficarei muito agradecido. Porém, como há dados completos para 2018, será possível verificar a correção do cálculo pelo menos para este ano.

A API de que precisamos é implementada na forma de solicitações HTTP, onde os parâmetros são transmitidos via url e uma chave de acesso especial é indicada no cabeçalho. Declare a lógica correspondente:
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();

Todas as ações de importação de dados serão colocadas no formulário de ligas criadas anteriormente. Lá colocaremos a chave de acesso na barra de ferramentas da tabela com a lista de ligas:
EXTEND FORM leagues
PROPERTIES () key DRAW o TOOLBAR
;


Primeiro, implementamos a lista de ligas. Para isso, a API do Football possui um URL especial: / ligas. Uma solicitação GET para ele retorna JSON do formulário:

A resposta
 { "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 } ] } } 


Para gerar uma solicitação GET e registrar o corpo da resposta, a seguinte construção é usada:
LOCAL result = FILE ();
EXTERNAL HTTP GET url() + '/leagues' HEADERS headers TO result;

Ele grava o resultado na propriedade local result sem parâmetros do tipo FILE.

Para analisar um arquivo no formato JSON, é criado um formulário cuja estrutura corresponde à estrutura JSON. Você pode gerá-lo no IDE usando o item de menu:

imagem

Para o JSON acima, o formulário será semelhante a este (levando em consideração apenas os valores que serão importados):
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
;

Para importar diretamente da propriedade de resultado JSON no formato do formulário importLeagues, use o seguinte comando:
IMPORT importLeagues JSON FROM result();

Após sua execução, os valores correspondentes do arquivo JSON serão colocados nas propriedades tournamentName , seasonName e leagueId :

imagem

Ou seja, o valor do nome do torneio (0) será "Copa do Mundo" e, no nome do torneio (1), será "Premier League".

Infelizmente, o API Football não possui uma entidade de torneio. A única maneira de vincular todas as ligas é ter um nome que corresponda às ligas do mesmo torneio de diferentes épocas do ano. Para isso, na importação, primeiro agrupamos todos os nomes das ligas importadas e, se não estiver no banco de dados, criamos novos torneios:
FOR [ GROUP SUM 1 BY tournamentName( INTEGER i)]( STRING tn) AND NOT tournament(tn) DO NEW t = Tournament {
name(t) <- tn;
}

Também não há códigos para as estações do ano, portanto, ao importar ligas, elas são criadas da mesma maneira. Após a criação dos objetos ausentes, as ligas são importadas diretamente. Os torneios e as temporadas são pesquisados ​​por nome usando as propriedades criadas anteriormente pelo 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));
}

Por padrão, os dados serão carregados, mas serão salvos no banco de dados somente quando o usuário clicar no botão Salvar no formulário. Se necessário, você pode adicionar o comando APPLY no final da ação para que ele seja salvo imediatamente no banco de dados sem uma visualização.

E, finalmente, adicione a ação de importação ao formulário da lista de ligas:
EXTEND FORM leagues
PROPERTIES () importLeagues DRAW o TOOLBAR
;

Da mesma forma, importamos clubes e partidas. No entanto, como a API fornece a capacidade de importá-los apenas para uma liga específica, a ação deve levar a liga como uma entrada:

Importar clubes e partidas
//
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);
}
}

Há uma peculiaridade para as partidas: os códigos dos times são incluídos nas tags homeTeam e awayTeam adicionais . Grupos correspondentes são criados para eles por analogia com api. Além disso, eles têm as mesmas tags team_id . Como as propriedades com o mesmo nome não podem ser incluídas no formulário, é usada a palavra-chave EXTID especial, que define o nome da marca no JSON importado.

Para que todas as importações estejam na mesma forma e, como estão vinculadas às ligas, as levamos para a mesma forma. Além disso, adicionamos equipes e correspondências ao formulário para poder ver o que é importado antes de salvar:
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);
}
}
}


O formulário resultante terá a seguinte aparência:
imagem

Todas as importações serão colocadas em um módulo APIFootball separado.

Cálculo do coeficiente


Prosseguimos diretamente no cálculo do coeficiente de país da UEFA. É lógico colocar todo o código em um módulo da UEFA especialmente instalado para isso.

Primeiro, lembre-se de que a API Football fornece uma interface para importar todas as partidas, e não apenas as Eurocups. Portanto, separamos as partidas da Eurocup de acordo com o nome do torneio (é mais correto ter uma propriedade primária separada para isso, mas a implementação das propriedades sempre pode ser alterada sem modificar o restante da lógica):
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));

Primeiro, vamos calcular os pontos que cada clube recebe em uma temporada pelos resultados de partidas específicas.
Durante esse período, cada equipe recebe:
2 pontos em caso de vitória;
1 ponto em caso de empate.
Desde 1999, esses pontos são divididos em dois se forem conquistados em rodadas de qualificação, ou seja:
1 ponto em caso de vitória;
0,5 pontos por empate.


Crie propriedades auxiliares que determinam a relação entre a partida e o clube:
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);

Para determinar quantos pontos são marcados em cada partida, adicionamos a propriedade principal do tipo numérico da rodada, que por padrão será igual a um:
dataMatchCoeff = DATA NUMERIC [ 10 , 1 ] (Round);
matchCoeff ' ' (Round r) = OVERRIDE dataMatchCoeff(r), 1.0 ;

Depois, contamos pontos para vitórias e empates e somamos:
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 ;

Os pontos para as partidas são marcados como MATERIALIZED, para que sejam salvos na tabela e não sejam calculados sempre.

Agora você precisa contar pontos de bônus:
Além disso, pontos de bônus são concedidos:
1 ponto é dado caso a equipe chegue às quartas de final, semifinais e finais nas Copas da Europa;
4 pontos por alcançar a fase de grupos da Liga dos Campeões (até 1996 - 2 pontos, de 1997 a 2003 - 1 ponto, de 2004 a 2008 - 3 pontos);
5 pontos no caso de uma equipe sair para a 1/8 de final da Liga dos Campeões (antes de 2008 - 1 ponto).
Apenas partidas jogadas são levadas em consideração (perdas técnicas não são levadas em consideração). As partidas que terminam em uma série de pênaltis, no cálculo do coeficiente, são consideradas de acordo com o resultado, que é fixado pelos resultados do jogo no horário principal e no prolongamento.

Nesta implementação, assumimos que o clube entrou na rodada do torneio se ele jogou pelo menos uma partida. Para isso, calculamos quantas partidas o clube disputou em uma temporada, torneio ou rodada específica:
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;


Agora você precisa determinar quantos pontos serão marcados para a passagem em uma rodada específica. Uma vez que depende do torneio (por exemplo, uma passagem na Liga dos Campeões recebe 5 pontos, mas nada na Liga Europa). Para fazer isso, apresentamos a propriedade principal:
bonusPoints ' ' = DATA NUMERIC [ 10 , 1 ] (Tournament, Round);

Agora vamos calcular pontos de bônus e o número total de pontos do clube para a temporada:
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);

Por fim, vamos diretamente ao coeficiente do país.
Para calcular a classificação da associação, todos os pontos marcados pelos clubes participantes da Liga dos Campeões e da Liga Europa são somados, e o resultado é dividido pelo número de clubes dessa associação [2] [3].

Vamos calcular o número de clubes de cada associação que participou de competições europeias:
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;

Agora, consideramos o número total de pontos de associação para a temporada e dividimos pelo número de clubes:
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 );


A classificação de um país é a soma dos coeficientes do país nos últimos 5 anos.


Para fazer isso, numeramos todas as estações começando do último pelo código interno (assumimos que o último foi adicionado posteriormente e possui um código maior):
index '' (Season s) = PARTITION SUM 1 IF s IS Season ORDER DESC s;

Se necessário, você pode inserir um campo ou número separado por nome.
Resta apenas calcular a classificação final para o país:
rating '' (Country c) = GROUP SUM points(Season s, c) IF index(s) <= 5 ;

Acima, anunciamos chances de torneios e rodadas. Adicione-os ao formulário de edição de torneios, enquanto filtra apenas as rodadas que ocorreram nesses torneios:
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)
;


Configurações de probabilidades, por exemplo, para a Liga dos Campeões que você precisa definir assim:
imagem

Vamos desenhar um formulário que exibirá a classificação, onde as equipes serão mostradas para cada país e para cada equipe suas partidas:
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;
}

O formulário resultante terá a seguinte aparência:
imagem
A cor nas tabelas dos clubes mostra quando ele participou das temporadas e da tabela de jogos - quem venceu.
A imagem mostra que as classificações para 2018 são calculadas exatamente da mesma forma que na Wikipedia. Nos anos anteriores, como mencionado acima, o Football API não fornece todas as informações.

Sumário



Criamos um pequeno aplicativo que é totalmente descrito pelo código acima e armazena seus dados no PostgreSQL, fornece uma interface da web para visualizar e editar dados. Ao mesmo tempo, funcionará efetivamente em grandes volumes, pois todos os formulários lêem apenas a janela visível. Também estão disponíveis filtros, classificação, carregamento para o Excel e muito mais.

Deve-se notar a facilidade com que, usando a plataforma, a tarefa de calcular o coeficiente foi decomposta em propriedades individuais. Quando executada, toda essa lógica será convertida em consultas SQL e todos os cálculos serão executados diretamente no servidor de banco de dados usando todas as otimizações do DBMS.

Um exemplo de como o aplicativo funciona com os dados carregados nele pode ser encontrado em: https://demo.lsfusion.org/euroleague . Login de convidado sem senha. O usuário está no modo somente leitura.

Quem desejar pode definir tudo localmente e, por exemplo, modelar os coeficientes inserindo os resultados de futuras correspondências. Todos os módulos de aplicativos descritos acima estão hospedados no github . Após a instalação automática, você só precisa exibir esses arquivos na pasta apropriada nas instruções e reiniciar o servidor.

Para fazer o download de dados da API do Football, você precisa se registrar com eles e obter a chave da API. Requer um cartão, mas se você não fizer mais de 50 solicitações por dia, nada será deduzido dele.

Além disso, você pode executar esse aplicativo online na seção apropriada do site. Na guia Plataforma, selecione o exemplo de cálculo de probabilidades da UEFA e clique em Reproduzir.

A propósito, se alguém precisar implementar algum sistema simples para o qual o Excel não seja mais adequado, escreva nos comentários. Para conhecer os recursos da plataforma, tentaremos implementá-la e escrever o artigo correspondente.

Source: https://habr.com/ru/post/pt467279/


All Articles