Initial commit

This commit is contained in:
Noah Spannbauer
2020-11-24 10:52:19 -06:00
commit 7580f84420
11 changed files with 752 additions and 0 deletions

264
.gitignore vendored Normal file
View File

@@ -0,0 +1,264 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Azure Functions localsettings file
local.settings.json
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

6
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions",
"ms-dotnettools.csharp"
]
}

11
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to .NET Functions",
"type": "coreclr",
"request": "attach",
"processId": "${command:azureFunctions.pickProcess}"
}
]
}

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"azureFunctions.deploySubpath": "bin/Release/netcoreapp3.1/publish",
"azureFunctions.projectLanguage": "C#",
"azureFunctions.projectRuntime": "~3",
"debug.internalConsoleOptions": "neverOpen",
"azureFunctions.preDeployTask": "publish"
}

69
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,69 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "clean",
"command": "dotnet",
"args": [
"clean",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet",
"args": [
"build",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"dependsOn": "clean",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "clean release",
"command": "dotnet",
"args": [
"clean",
"--configuration",
"Release",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"args": [
"publish",
"--configuration",
"Release",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"dependsOn": "clean release",
"problemMatcher": "$msCompile"
},
{
"type": "func",
"dependsOn": "build",
"options": {
"cwd": "${workspaceFolder}/bin/Debug/netcoreapp3.1"
},
"command": "host start",
"isBackground": true,
"problemMatcher": "$func-watch"
}
]
}

170
GetGames.cs Normal file
View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Client;
using Microsoft.Graph;
using Newtonsoft.Json;
namespace azfx_calendars_functions_dotnet
{
public static class GetGames
{
public static readonly HttpClient client = new HttpClient();
[FunctionName("GetGames")]
public static async Task Run([TimerTrigger("0 30 19 * * *")]TimerInfo myTimer, ILogger log)
{
try
{
HttpResponseMessage teamsResponse = await client.GetAsync("https://statsapi.mlb.com/api/v1/teams?sportId=1");
string teamsResponseBody = await teamsResponse.Content.ReadAsStringAsync();
Teams.Root teams = JsonConvert.DeserializeObject<Teams.Root>(teamsResponseBody);
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
string url = String.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", Environment.GetEnvironmentVariable("TENANT_ID"));
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder.Create(Environment.GetEnvironmentVariable("CLIENT_ID")).WithClientSecret(Environment.GetEnvironmentVariable("CLIENT_SECRET")).WithAuthority(url).Build();
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (HttpRequestMessage requestMessage) => {
AuthenticationResult authenticationResult = await confidentialClientApplication.AcquireTokenForClient(scopes).ExecuteAsync();
requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
}));
foreach (azfx_calendars_functions_dotnet.Teams.Team team in teams.teams)
{
string teamId = team.id.ToString();
string teamName = team.name;
string requestUri = string.Format("https://statsapi.mlb.com/api/v1/schedule?sportId=1&teamId={0}&startDate={1}&endDate={2}", teamId, Environment.GetEnvironmentVariable("START_DATE"), Environment.GetEnvironmentVariable("END_DATE"));
HttpResponseMessage response = await client.GetAsync(requestUri);
string responseBody = await response.Content.ReadAsStringAsync();
Schedule.Root schedule = JsonConvert.DeserializeObject<Schedule.Root>(responseBody);
string siteName = team.name.Replace(" ", String.Empty);
string serverRelativeUrl = string.Format("/sites/{0}", siteName);
foreach (Schedule.Date date in schedule.dates)
{
foreach (Schedule.Game game in date.games)
{
string opponent = game.teams.home.team.id == team.id ? game.teams.away.team.name : game.teams.home.team.name;
Boolean isHomeTeam = game.teams.home.team.id == team.id ? true : false;
Dictionary<string, object> additionalData = new Dictionary<string, object>();
List<string> gameType = new List<string>();
string gamePk = game.gamePk.ToString();
string filter = string.Format("fields/GamePK eq {0}", gamePk);
List<QueryOption> queryOptions = new List<QueryOption>()
{
new QueryOption("expand", "fields(select=GamePK,Title,Location,GameType,GameStatus,StartDate,EndDate,HomeTeamScore,AwayTeamScore,GamePK)"),
new QueryOption("filter", filter)
};
IListItemsCollectionPage listItems = await graphServiceClient.Sites["noahspan.sharepoint.com"].SiteWithPath(serverRelativeUrl).Lists["Games"].Items.Request(queryOptions).GetAsync();
switch(game.gameType)
{
case "E":
gameType.Add("Exhibition");
break;
case "S":
gameType.Add("Spring Training");
break;
case "R":
gameType.Add("Regular Season");
break;
case "D":
gameType.Add("Division");
break;
case "L":
gameType.Add("League Championship Series");
break;
case "W":
gameType.Add("World Series");
break;
default:
break;
}
additionalData.Add("Title", opponent);
additionalData.Add("IsHomeTeam", isHomeTeam);
additionalData.Add("Location", game.venue.name);
additionalData.Add("GameType", gameType[0].ToString());
additionalData.Add("GameStatus", game.status.detailedState);
additionalData.Add("StartDate", game.gameDate);
additionalData.Add("EndDate", Convert.ToDateTime(game.gameDate).ToUniversalTime().AddHours(3).ToString("yyyy-MM-ddTHH:mm:ssZ"));
additionalData.Add("GamePK", gamePk);
if (game.teams.home.score != null)
{
additionalData.Add("HomeTeamScore", game.teams.home.score);
}
if (game.teams.away.score != null)
{
additionalData.Add("AwayTeamScore", game.teams.away.score);
}
if (game.rescheduleDate != null)
{
additionalData.Add("RescheduleDate", game.rescheduleDate);
}
if (game.rescheduledFrom != null)
{
additionalData.Add("RescheduledFrom", game.rescheduledFrom);
}
if (listItems.Count > 0) {
foreach (ListItem listItem in listItems)
{
IDictionary<string, object> listItemFields = listItem.Fields.AdditionalData;
Dictionary<string, object> fieldValuesToUpdate = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> entry in listItemFields) {
if (entry.Key != "@odata.etag") {
var listItemFieldValue = entry.Value.ToString();
var additionalDataKeyValue = additionalData[entry.Key].ToString();
if (listItemFieldValue != additionalDataKeyValue) {
fieldValuesToUpdate.Add(entry.Key, additionalDataKeyValue);
}
}
}
if (fieldValuesToUpdate.Count > 0)
{
FieldValueSet fieldValueSet = new FieldValueSet
{
AdditionalData = fieldValuesToUpdate
};
await graphServiceClient.Sites["noahspan.sharepoint.com"].SiteWithPath(serverRelativeUrl).Lists["Games"].Items[listItem.Id].Fields.Request().UpdateAsync(fieldValueSet);
log.LogInformation(string.Format("{0} game {1} was updated", teamName, gamePk));
}
}
} else {
ListItem listItem = new ListItem
{
Fields = new FieldValueSet
{
AdditionalData = additionalData
}
};
await graphServiceClient.Sites["noahspan.sharepoint.com"].SiteWithPath(serverRelativeUrl).Lists["Games"].Items.Request().AddAsync(listItem);
log.LogInformation(string.Format("{0} game {1} was added", teamName, gamePk));
}
}
}
}
}
catch (HttpRequestException e)
{
log.LogInformation("Error: " + e.Message);
}
finally
{
log.LogInformation("Timer job completed");
}
}
}
}

0
README.md Normal file
View File

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<RootNamespace>azfx_calendars_functions_dotnet</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Graph" Version="3.17.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.19.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>

11
host.json Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
}
}
}

125
models/Schedule.cs Normal file
View File

@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
namespace azfx_calendars_functions_dotnet.Schedule
{
public class Status {
public string abstractGameState { get; set; }
public string codedGameState { get; set; }
public string detailedState { get; set; }
public string statusCode { get; set; }
public string abstractGameCode { get; set; }
public string reason { get; set; }
public bool? startTimeTBD { get; set; }
}
public class LeagueRecord {
public int wins { get; set; }
public int losses { get; set; }
public string pct { get; set; }
}
public class Team {
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Away {
public LeagueRecord leagueRecord { get; set; }
public int? score { get; set; }
public Team team { get; set; }
public bool isWinner { get; set; }
public bool splitSquad { get; set; }
public int? seriesNumber { get; set; }
}
public class LeagueRecord2 {
public int wins { get; set; }
public int losses { get; set; }
public string pct { get; set; }
}
public class Team2 {
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Home {
public LeagueRecord2 leagueRecord { get; set; }
public int? score { get; set; }
public Team2 team { get; set; }
public bool isWinner { get; set; }
public bool splitSquad { get; set; }
public int? seriesNumber { get; set; }
}
public class Teams {
public Away away { get; set; }
public Home home { get; set; }
}
public class Venue {
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Content {
public string link { get; set; }
}
public class Game {
public int gamePk { get; set; }
public string link { get; set; }
public string gameType { get; set; }
public string season { get; set; }
public string gameDate { get; set; }
public string officialDate { get; set; }
public Status status { get; set; }
public Teams teams { get; set; }
public Venue venue { get; set; }
public Content content { get; set; }
public bool isTie { get; set; }
public int gameNumber { get; set; }
public bool publicFacing { get; set; }
public string doubleHeader { get; set; }
public string gamedayType { get; set; }
public string tiebreaker { get; set; }
public string calendarEventID { get; set; }
public string seasonDisplay { get; set; }
public string dayNight { get; set; }
public string description { get; set; }
public int scheduledInnings { get; set; }
public bool reverseHomeAwayStatus { get; set; }
public int inningBreakLength { get; set; }
public string seriesDescription { get; set; }
public string recordSource { get; set; }
public string ifNecessary { get; set; }
public string ifNecessaryDescription { get; set; }
public int? gamesInSeries { get; set; }
public int? seriesGameNumber { get; set; }
public DateTime? rescheduleDate { get; set; }
public DateTime? rescheduledFrom { get; set; }
}
public class Date {
public string date { get; set; }
public int totalItems { get; set; }
public int totalEvents { get; set; }
public int totalGames { get; set; }
public int totalGamesInProgress { get; set; }
public List<Game> games { get; set; }
public List<object> events { get; set; }
}
public class Root {
public string copyright { get; set; }
public int totalItems { get; set; }
public int totalEvents { get; set; }
public int totalGames { get; set; }
public int totalGamesInProgress { get; set; }
public List<Date> dates { get; set; }
}
}

68
models/Teams.cs Normal file
View File

@@ -0,0 +1,68 @@
using System.Collections.Generic;
namespace azfx_calendars_functions_dotnet.Teams
{
public class Venue
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class League
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Sport
{
public int id { get; set; }
public string link { get; set; }
public string name { get; set; }
}
public class SpringLeague
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
public string abbreviation { get; set; }
}
public class Team
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
public int season { get; set; }
public Venue venue { get; set; }
public string teamCode { get; set; }
public string fileCode { get; set; }
public string abbreviation { get; set; }
public string teamName { get; set; }
public string locationName { get; set; }
public string firstYearOfPlay { get; set; }
public League league { get; set; }
public Division division { get; set; }
public Sport sport { get; set; }
public string shortName { get; set; }
public SpringLeague springLeague { get; set; }
public string allStarStatus { get; set; }
public bool active { get; set; }
}
public class Root
{
public string copyright { get; set; }
public List<Team> teams { get; set; }
}
}