Cálculo de la relación de países de la UEFA

imagen

Hay dos torneos populares en el fútbol: la Champions League y la Europa League. En función de sus resultados, se calcula la denominada Clasificación de las asociaciones de fútbol . Según esta clasificación, se determina además cuántos equipos de cada país participarán en torneos posteriores.

En este artículo, crearé una aplicación basada en la plataforma lsFusion abierta y gratuita que calculará esta calificación. Almacenará todos sus datos en PostgreSQL, proporcionará una interfaz web para cambiarlos y mostrarlos con capacidades de filtrado y clasificación, así como también importará resultados de coincidencias utilizando una API especial.

Todo el código para implementar esta aplicación constará de unas 300 líneas significativas.

Lógica de dominio


La creación de cualquier sistema de información comienza con la tarea de la lógica de dominio.
En primer lugar, es lógico seleccionar los directorios más simples que solo tienen un código y un nombre:

  • Torneo Liga de Campeones o Europa League.
  • Temporada 2018-2019 / 2017-2018 etc.
  • Ronda . Final, Semifinal, Fase de grupos, etc. Se puede considerar como una composición para el torneo, pero en esta implementación lo destaqué como una entidad separada.
  • Pais Esta aplicación se utiliza como la Asociación de Fútbol. Por ejemplo, el club de Mónaco se encuentra en el país de Mónaco, pero juega en el campeonato francés.
  • Club Barcelona, ​​Real Madrid, Manchester United, etc.

Dado que lsFusion usa el mismo tipo de lógica para declararlos, declararemos un metacódigo (o plantilla de código) que generará la lógica correspondiente:

Metacódigo de declaración de directorio
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


Él anunciará:

  • Clase con nombre de pila
  • Propiedades con código y nombre para la nueva clase.
  • Tres formas: editar un objeto, una forma con una lista de todos los objetos, que luego se agrega al navegador, un diálogo para seleccionar este objeto. Como diálogo, puede usar la segunda forma, pero luego el usuario tendrá la oportunidad de cambiar los objetos al elegir, lo que puede provocar errores por parte de los usuarios.

Se pasan cuatro parámetros al metacódigo:

  • Identificador (objeto) . Con este nombre se crearán clases y formularios. La construcción ### se usa para hacer que la primera letra del identificador se capitalice en el código resultante.
  • Nombre en singular . Usado para el título de la clase y la forma.
  • El nombre está en plural . Utilizado para cuadro combinado y diálogo.
  • Longitud del nombre En los nombres de diferentes objetos, se esperan diferentes longitudes, lo cual es importante al construir la interfaz.

Usando el metacódigo creado, agregue las cinco entidades descritas anteriormente:
@defineMasterObject (tournament, '' , '' , 20 );
@defineMasterObject (season, '' , '' , 5 );
@defineMasterObject (round, '' , '' , 15 );
@defineMasterObject (country, '' , '' , 10 );
@defineMasterObject (team, '' , '' , 20 );


El código generado, por ejemplo, para un torneo se verá así:
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;
}

Agregue el enlace al país a la lógica del club generada. Para hacer esto, primero cree la propiedad correspondiente, que luego colocará en los formularios de edición y visualización del club:
country = DATA Country (Team);
nameCountry '' (Team t) = name(country(t));

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

Ponemos toda la lógica creada en un módulo maestro separado (archivo Master.lsf).

Ahora crea una entidad League . Ella determinará el torneo de una temporada en particular. Por ejemplo, Champions League 2017-18 o Europa League 2018-19. La liga no tendrá un nombre, sino solo enlaces al torneo y la temporada. Por lo tanto, no usaremos el metacódigo anterior, pero haremos la misma lógica y lo pondremos en el nuevo módulo de la Liga:
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;
}

Y finalmente, agregue la lógica de los partidos. Para hacer esto, cree la clase Match , que se referirá a la liga y la ronda. Para él, los clubes que participaron en él, y el resultado también se le preguntará. Ponemos todo esto en un 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;
}


Importación de datos


Desafortunadamente, logré encontrar solo una API pública y gratuita que sea compatible con todas las Eurocopas. Esta es la API de fútbol . Sin embargo, hay problemas:

  • No hay resultados hasta 2016.
  • No hay calificaciones para la Europa League hasta 2018.
  • Hay ciertos errores en los datos. Por ejemplo, Irtysh Pavlodar está asignado a Rusia, aunque este club representa a Kazajstán. Además, Europa Fc por alguna razón se refiere a España en lugar de Gibraltar.


Los errores de datos pueden corregirse manualmente utilizando formularios creados previamente. Sin embargo, dado que el cálculo del coeficiente total se basa en los últimos cinco años, desafortunadamente, no funcionará calcular el coeficiente total a partir de los datos de API Football. Si alguien en los comentarios sugiere dónde obtener los datos necesarios en cualquier formato de años anteriores, estaré muy agradecido. Pero, dado que hay datos completos para 2018, será posible verificar la exactitud del cálculo para al menos este año.

La API que necesitamos se implementa en forma de solicitudes HTTP, donde los parámetros se transmiten a través de la URL y se indica una clave de acceso especial en el encabezado. Declare la lógica correspondiente:
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 las acciones de importación de datos se colocarán en el formulario de ligas creado anteriormente. Allí colocaremos la clave de acceso en la barra de herramientas de la tabla con la lista de ligas:
EXTEND FORM leagues
PROPERTIES () key DRAW o TOOLBAR
;


Primero, implementamos la lista de ligas. Para esto, la API de fútbol tiene una url especial: / ligas. Una solicitud GET le devuelve JSON del formulario:

La respuesta
 { "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 generar una solicitud GET y registrar el cuerpo de respuesta, se utiliza la siguiente construcción:
LOCAL result = FILE ();
EXTERNAL HTTP GET url() + '/leagues' HEADERS headers TO result;

Escribe el resultado en la propiedad de resultado local sin parámetros de tipo FILE.

Para analizar un archivo en formato JSON, se crea un formulario cuya estructura corresponde a la estructura JSON. Puede generarlo en el IDE utilizando el elemento del menú:

imagen

Para el JSON anterior, el formulario se verá así (teniendo en cuenta solo aquellos valores que se importarán):
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 directamente desde la propiedad del resultado JSON en el formato del formulario importLeagues, use el siguiente comando:
IMPORT importLeagues JSON FROM result();

Después de su ejecución, los valores correspondientes del archivo JSON se colocarán en las propiedades torneoName , seasonName y leagueId :

imagen

Es decir, el valor de torneoName (0) será "Copa Mundial", y en torneoName (1) será "Premier League".

Desafortunadamente, API Football no tiene una entidad de torneo en absoluto. La única forma de vincular todas las ligas es tener un nombre que coincida con las ligas del mismo torneo de diferentes temporadas. Para hacer esto, en la importación, primero agrupamos todos los nombres de las ligas importadas y, si no en la base de datos, creamos nuevos torneos:
FOR [ GROUP SUM 1 BY tournamentName( INTEGER i)]( STRING tn) AND NOT tournament(tn) DO NEW t = Tournament {
name(t) <- tn;
}

Tampoco hay códigos para las estaciones, por lo que al importar ligas, se crean de la misma manera. Después de crear los objetos que faltan, las ligas se importan directamente. Los torneos y las temporadas se buscan por nombre utilizando las propiedades creadas anteriormente a través de 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 defecto, los datos se cargarán, pero solo se guardarán en la base de datos cuando el usuario haga clic en el botón Guardar en el formulario. Si es necesario, puede agregar el comando APLICAR al final de la acción para que se guarde inmediatamente en la base de datos sin una vista previa.

Y finalmente, agregue la acción de importación al formulario de lista de liga:
EXTEND FORM leagues
PROPERTIES () importLeagues DRAW o TOOLBAR
;

Del mismo modo, importamos clubes y partidos. Sin embargo, dado que la API proporciona la capacidad de importarlos solo para una liga específica, la acción debe tomar la liga como entrada:

Importar clubes y partidos
//
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);
}
}

Hay una peculiaridad para los partidos: los códigos de equipo van dentro de las etiquetas adicionales de homeTeam y awayTeam . Los grupos correspondientes se crean para ellos por analogía con api. Además, en su interior tienen las mismas etiquetas team_id . Como las propiedades con el mismo nombre no se pueden agregar al formulario, se usa la palabra clave especial EXTID , que define el nombre de la etiqueta en el JSON importado.

Para que todas las importaciones estén en la misma forma, y ​​dado que están vinculadas a las ligas, las llevamos todas a la misma forma. Además, agregamos equipos y partidos al formulario para poder ver lo que se importa antes de guardar:
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);
}
}
}


La forma resultante se verá así:
imagen

Todas las importaciones se colocarán en un módulo APIFootball separado.

Cálculo del coeficiente


Procedemos directamente al cálculo del coeficiente de país de la UEFA. Es lógico poner todo el código en un módulo UEFA especialmente instalado para esto.

Primero, tenga en cuenta que Football API proporciona una interfaz para importar todos los partidos, y no solo las Eurocopas. Por lo tanto, separamos los partidos de Eurocup según el nombre del torneo (es más correcto tener una propiedad primaria separada para esto, pero la implementación de las propiedades siempre se puede cambiar sin modificar el resto de la 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));

Primero, calculemos los puntos que cada club recibe en una temporada por los resultados de partidos específicos.
Durante este período, cada equipo recibe:
2 puntos en caso de victoria;
1 punto en caso de empate.
Desde 1999, estos puntos se dividen en dos si se obtienen en rondas de calificación, es decir:
1 punto en caso de victoria;
0.5 puntos por un empate.


Crea propiedades auxiliares que determinen la relación entre el partido y el club:
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 cuántos puntos se anotan en cada partido, agregamos la propiedad primaria del tipo numérico para la ronda, que por defecto será igual a uno:
dataMatchCoeff = DATA NUMERIC [ 10 , 1 ] (Round);
matchCoeff ' ' (Round r) = OVERRIDE dataMatchCoeff(r), 1.0 ;

Luego contamos puntos por victorias y empates y sumamos:
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 ;

Los puntos para los partidos se marcan como MATERIALIZADOS para que se guarden en la tabla y no se calculen cada vez.

Ahora necesita contar puntos de bonificación:
Además, se otorgan puntos de bonificación:
Se otorga 1 punto en caso de que el equipo llegue a cuartos de final, semifinales y finales en copas europeas;
4 puntos por alcanzar la fase de grupos de la Liga de Campeones (hasta 1996 - 2 puntos, de 1997 a 2003 - 1 punto, de 2004 a 2008 - 3 puntos);
5 puntos en caso de que un equipo se vaya a las finales de 1/8 de la Liga de Campeones (antes de 2008 - 1 punto).
Solo se tienen en cuenta los partidos jugados (no se tienen en cuenta las pérdidas técnicas). Los partidos que terminan en una serie de penaltis, cuando se calcula el coeficiente se consideran de acuerdo con el resultado, que se fija por los resultados del juego en el tiempo principal y extra.

En esta implementación, suponemos que el club entró en la ronda del torneo si jugó al menos un partido en él. Para hacer esto, calculamos cuántos partidos jugó el club en una temporada, torneo o ronda en particular:
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;


Ahora necesita determinar cuántos puntos anotar para el pasaje en una ronda en particular. Como depende del torneo (por ejemplo, un pasaje en la ⅛ Champions League recibe 5 puntos, pero nada en la Europa League). Para hacer esto, presentamos la propiedad primaria:
bonusPoints ' ' = DATA NUMERIC [ 10 , 1 ] (Tournament, Round);

Ahora calculemos los puntos de bonificación y el número total de puntos para el club para la 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);

Finalmente, vamos directamente al coeficiente del país.
Para calcular la calificación de la asociación, se suman todos los puntos anotados por los clubes que participan en la Champions League y la Europa League, y el resultado se divide por el número de clubes de esta asociación [2] [3].

Calculemos el número de clubes para cada asociación que participó en competiciones europeas:
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;

Ahora consideramos el número total de puntos de asociación para la temporada y dividimos por el 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 );


La calificación de un país es la suma de los coeficientes del país para los 5 años anteriores.


Para hacer esto, numeramos todas las estaciones comenzando desde la última por el código interno (asumimos que estas últimas se agregaron más tarde y tienen un código más grande):
index '' (Season s) = PARTITION SUM 1 IF s IS Season ORDER DESC s;

Si es necesario, puede ingresar un campo o número por nombre.
Solo queda calcular la calificación final para el país:
rating '' (Country c) = GROUP SUM points(Season s, c) IF index(s) <= 5 ;

Arriba, anunciamos probabilidades para torneos y rondas. Agréguelos al formulario de edición del torneo, mientras filtra solo las rondas que estaban en estos torneos:
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)
;


Configuración de probabilidades, por ejemplo, para la Liga de Campeones que necesita establecer de esta manera:
imagen

Dibujemos un formulario que muestre la calificación, donde se mostrarán los equipos para cada país y para cada equipo sus partidos:
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;
}

La forma resultante se verá así:
imagen
El color en las mesas de los clubes muestra cuándo participó en las temporadas, y en la tabla de partidos, quién ganó.
La imagen muestra que las calificaciones para 2018 se calculan exactamente igual que en Wikipedia. Para años anteriores, como se mencionó anteriormente, Football API no proporciona toda la información.

Resumen



Creamos una pequeña aplicación que está completamente descrita por el código anterior y almacena sus datos en PostgreSQL, proporciona una interfaz web para ver y editar datos. Al mismo tiempo, funcionará eficazmente en grandes volúmenes, ya que todos los formularios leen solo la ventana visible. También están listos los filtros, la clasificación, la carga en Excel y más.

Debe notarse cuán fácilmente usando la plataforma, la tarea de calcular el coeficiente se descompuso en propiedades individuales. Cuando se ejecute, toda esta lógica se traducirá en consultas SQL y todos los cálculos se realizarán directamente en el servidor de la base de datos utilizando todas las optimizaciones de DBMS.

Puede encontrar un ejemplo de cómo funciona la aplicación con los datos cargados en ella en: https://demo.lsfusion.org/euroleague . Inicio de sesión de invitado sin contraseña. El usuario está en modo de solo lectura.

Aquellos que lo deseen pueden establecer todo a nivel local y, por ejemplo, modelar los coeficientes ingresando los resultados de futuras coincidencias. Todos los módulos de aplicación descritos anteriormente están alojados en github . Después de la instalación automática, solo necesita abrir estos archivos en la carpeta correspondiente de las instrucciones y reiniciar el servidor.

Para descargar datos de Football API, debe registrarse con ellos y obtener la clave API. Requiere una tarjeta, pero si no realiza más de 50 solicitudes por día, no se deducirá nada de ella.

Además, puede ejecutar esta aplicación en línea en la sección correspondiente del sitio. En la pestaña Plataforma, seleccione el ejemplo de Cálculo de probabilidades de la UEFA y haga clic en Jugar.

Por cierto, si alguien necesita implementar un sistema simple para el que Excel ya no sea adecuado, escriba los comentarios. Para conocer las capacidades de la plataforma, intentaremos implementarla y escribir el artículo correspondiente.

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


All Articles