حساب نسبة البلد UEFA

صورة

هناك بطولتان رائعتان في كرة القدم: دوري الأبطال ودوري أوروبا. بناءً على نتائجها ، يتم احتساب ما يسمى تصنيف اتحادات كرة القدم . بناءً على هذا التصنيف ، يتم تحديد عدد الفرق من كل بلد التي ستشارك في البطولات التالية.

في هذه المقالة ، سوف أقوم بإنشاء تطبيق يستند إلى نظام lsFusion المفتوح والمجاني الذي سيقوم بحساب هذا التصنيف. سيقوم بتخزين جميع بياناته في PostgreSQL ، ويوفر واجهة ويب لتغييرها وعرضها مع إمكانات التصفية والفرز ، وكذلك استيراد نتائج المطابقة باستخدام واجهة برمجة تطبيقات خاصة.

ستتألف كل شفرة تنفيذ هذا التطبيق من حوالي 300 سطر مهم.

منطق المجال


يبدأ إنشاء أي نظام معلومات بمهمة منطق المجال.
بادئ ذي بدء ، من المنطقي تفرد أبسط الدلائل التي تحتوي فقط على رمز واسم:

  • البطولة. دوري أبطال أوروبا أو دوري أوروبا.
  • الموسم . 2018-2019 / 2017-2018 إلخ.
  • جولة . النهائي ، نصف النهائي ، مرحلة المجموعات ، إلخ. يمكن اعتباره تركيبة للبطولة ، لكن في هذا التطبيق ، حددتها ككيان منفصل.
  • البلد. يستخدم هذا التطبيق باسم اتحاد كرة القدم. على سبيل المثال ، يقع نادي موناكو في بلد موناكو ، لكنه يلعب في البطولة الفرنسية.
  • النادي. برشلونة ، ريال مدريد ، مانشستر يونايتد ، إلخ.

نظرًا لأن lsFusion يستخدم نفس النوع من المنطق للإعلان عنها ، فسوف نعلن عن كود التعريف (أو قالب الشفرة) الذي سينشئ المنطق المقابل:

دليل تعريف Metacode
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


سيعلن:

  • فئة مع الاسم المحدد
  • خصائص مع رمز واسم للفئة الجديدة
  • ثلاثة أشكال: تحرير كائن ، نموذج يحتوي على قائمة بجميع الكائنات ، يتم إضافته بعد ذلك إلى لوحة التجول ، مربع حوار لتحديد هذا الكائن. كحوار ، يمكنك استخدام النموذج الثاني ، ولكن بعد ذلك ستتاح للمستخدم الفرصة لتغيير الكائنات عند الاختيار ، مما قد يؤدي إلى أخطاء من جانب المستخدمين.

يتم تمرير أربعة معلمات إلى metacode:

  • معرف (كائن) . مع هذا الاسم سيتم إنشاء الطبقات والنماذج. يتم استخدام بناء ### لجعل الحرف الأول من المعرف بالأحرف الكبيرة في التعليمات البرمجية الناتجة.
  • الاسم في صيغة المفرد . تستخدم لعنوان الطبقة والشكل.
  • الاسم في صيغة الجمع . تستخدم لمربع التحرير والسرد والحوار.
  • طول الاسم . في أسماء الكائنات المختلفة ، من المتوقع أطوال مختلفة ، وهو أمر مهم عند بناء الواجهة.

باستخدام الرمز الرئيسي الذي تم إنشاؤه ، أضف الكيانات الخمسة الموضحة أعلاه:
@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.lsf).

الآن إنشاء رابطة الكيان. ستحدد البطولة لموسم معين. على سبيل المثال ، Champions League 2017-18 أو Europa League 2018-19. الدوري لن يكون له اسم ، ولكن فقط روابط إلى البطولة والموسم. لذلك ، لن نستخدم كود metacode السابق ، لكننا سنفعل نفس المنطق ونضعه في وحدة الجامعة الجديدة:
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;
}

وأخيراً ، أضف منطق المباريات. للقيام بذلك ، قم بإنشاء " تطابق الفصل" ، والذي سوف يشير إلى الدوري والجولة. بالنسبة له ، الأندية التي شاركت فيه ، والنتيجة سوف تُسأل أيضًا. نضع كل هذا في وحدة مطابقة منفصلة:
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;
}


استيراد البيانات


لسوء الحظ ، تمكنت من العثور على واجهة برمجة تطبيقات (API) عامة ومجانية واحدة تدعم جميع برامج Eurocups. هذا هو API لكرة القدم . ومع ذلك ، هناك مشاكل:

  • لا توجد نتائج حتى عام 2016.
  • لا توجد مؤهلات لدوري أوروبا حتى عام 2018.
  • هناك بعض الأخطاء في البيانات. على سبيل المثال ، تم تعيين Irtysh Pavlodar لروسيا ، على الرغم من أن هذا النادي يمثل كازاخستان. أيضًا ، يشير Europa Fc لسبب ما إلى إسبانيا بدلاً من جبل طارق.


يمكن تصحيح أخطاء البيانات يدويًا باستخدام النماذج التي تم إنشاؤها مسبقًا. ومع ذلك ، نظرًا لأن حساب المعامل الكلي يستند إلى السنوات الخمس الماضية ، لسوء الحظ ، لن يعمل لحساب المعامل الكلي من بيانات API Football. إذا اقترح شخص ما في التعليقات مكان الحصول على البيانات الضرورية بأي تنسيق من السنوات السابقة ، فسأكون ممتنًا للغاية. ولكن نظرًا لوجود بيانات كاملة لعام 2018 ، سيكون من الممكن التحقق من صحة الحساب خلال هذا العام على الأقل.

يتم تطبيق واجهة برمجة التطبيقات التي نحتاجها في شكل طلبات 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 على رابط خاص: / بطولات الدوري. طلب 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
;

للاستيراد مباشرة من خاصية نتيجة JSON بتنسيق نموذج importL زمالات ، استخدم الأمر التالي:
IMPORT importLeagues JSON FROM result();

بعد تنفيذه ، سيتم وضع القيم المطابقة من ملف JSON في خصائص tourNameName و seasonName و leagueId :

صورة

أي أن قيمة البطولة (0) ستكون "كأس العالم" ، وفي البطولة اسم (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
;

وبالمثل ، نحن نستورد الأندية والمباريات. ومع ذلك ، نظرًا لأن واجهة برمجة التطبيقات توفر القدرة على استيرادها فقط لدوري معين ، يجب أن يأخذ الإجراء الدوري كمدخل:

استيراد النوادي والمباريات
//
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);
}
}

هناك خصوصية للمباريات: رموز الفريق تذهب داخل homeTeam إضافية وعلامات بعيداTeam . يتم إنشاء مجموعات المقابلة لهم عن طريق القياس مع 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. لذلك ، نفصل بين مطابقات كأس الاتحاد الأوروبي وفقًا لاسم البطولة (من الأصح أن يكون لديك خاصية أساسية منفصلة لذلك ، ولكن يمكن دائمًا تغيير تنفيذ الخصائص دون تعديل بقية المنطق):
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 ، يتم تقسيم هذه النقاط إلى نقطتين إذا حصلوا عليها في جولات التأهيل ، أي:
نقطة واحدة في حالة النصر ؛
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);

لتحديد عدد النقاط التي يتم تسجيلها في كل تطابق ، نضيف الخاصية الأساسية من النوع الرقمي للجولة ، والتي ستكون افتراضيًا واحدة.
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 ;

يتم تمييز نقاط التطابق على أنها "مواد" بحيث يتم حفظها في الجدول ، ولا يتم احتسابها في كل مرة.

تحتاج الآن إلى حساب نقاط المكافآت:
بالإضافة إلى ذلك ، يتم منح نقاط المكافآت:
يتم إعطاء نقطة واحدة في حالة وصول الفريق إلى دور الثمانية ونصف النهائي والنهائي في الكؤوس الأوروبية ؛
4 نقاط للوصول إلى مرحلة المجموعات في دوري الأبطال (حتى 1996 - 2 نقطة ، من 1997 إلى 2003 - 1 نقطة ، من 2004 إلى 2008 - 3 نقاط) ؛
5 نقاط في حالة خروج الفريق إلى نهائيات 1/8 من دوري الأبطال (قبل 2008 - نقطة واحدة).
تؤخذ في الاعتبار المباريات التي لعبت فقط (لا تؤخذ الخسائر التقنية في الاعتبار). تعتبر المباريات المنتهية في سلسلة من ركلات الترجيح ، عند حساب المعامل ، مطابقة للنتيجة ، والتي يتم إصلاحها من خلال نتائج اللعبة في الوقت الرئيسي والإضافي.

في هذا التطبيق ، نفترض أن النادي دخل في جولة البطولة إذا لعب مباراة واحدة على الأقل فيها. للقيام بذلك ، نحسب عدد المباريات التي لعبها النادي في موسم معين أو دورة أو جولة:
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 );


تصنيف الدولة هو مجموع معاملات الدولة للسنوات الخمس السابقة.


للقيام بذلك ، نقوم بترقيم جميع الفصول التي تبدأ من الأخير بواسطة الكود الداخلي (نفترض أن الأخير قد تم إضافته لاحقًا وله رمز أكبر):
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 تُحسب تمامًا كما في ويكيبيديا. للسنوات السابقة ، كما ذكر أعلاه ، لا يوفر Football API جميع المعلومات.

يؤدي



لقد أنشأنا تطبيقًا صغيرًا موصوفًا بالكامل من خلال الرمز أعلاه ويخزن بياناته في PostgreSQL ، ويوفر واجهة ويب لعرض البيانات وتحريرها. في الوقت نفسه ، ستعمل بفعالية على وحدات التخزين الكبيرة ، نظرًا لأن جميع النماذج تقرأ فقط النافذة المرئية. كما يوجد خارج الصندوق عوامل تصفية وفرز وتحميل إلى Excel والمزيد.

تجدر الإشارة إلى مدى سهولة استخدام المنصة في تحليل مهمة حساب معامل إلى خصائص فردية. عند التنفيذ ، سيتم ترجمة كل هذا المنطق إلى استعلامات SQL ، وسيتم تنفيذ جميع العمليات الحسابية مباشرةً على خادم قاعدة البيانات باستخدام جميع عمليات تحسين قواعد البيانات.

يمكن العثور على مثال عن كيفية عمل التطبيق مع البيانات المحملة فيه على: https://demo.lsfusion.org/euroleague . تسجيل دخول ضيف بدون كلمة مرور. المستخدم في وضع القراءة فقط.

يمكن لأولئك الذين يرغبون في ضبط أنفسهم كل شيء محليًا ، وعلى سبيل المثال ، صياغة المعاملات من خلال إدخال نتائج المباريات المستقبلية. يتم استضافة جميع وحدات التطبيق المذكورة أعلاه على جيثب . بعد التثبيت التلقائي ، تحتاج فقط إلى ظهور هذه الملفات في المجلد المناسب من التعليمات وإعادة تشغيل الخادم.

من أجل تنزيل البيانات من Football API ، تحتاج إلى التسجيل معهم والحصول على مفتاح API. تتطلب بطاقة ، ولكن إذا لم تقدم أكثر من 50 طلبًا يوميًا ، فلن يتم خصم أي شيء منها.

بالإضافة إلى ذلك ، يمكنك تشغيل هذا التطبيق عبر الإنترنت في القسم المناسب على الموقع. في علامة التبويب النظام الأساسي ، حدد مثال حساب UEFA Odds ، ثم انقر فوق تشغيل.

بالمناسبة ، إذا احتاج شخص ما إلى تطبيق نظام بسيط لم يعد Excel مناسبًا له ، فاكتب في التعليقات. من أجل معرفة قدرات النظام الأساسي ، سنحاول تنفيذه وكتابة المقالة المقابلة.

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


All Articles