Florin Bobiș commited on
Commit
ec947b6
1 Parent(s): 93c9c23

added datasource filtering, updated db from seed

Browse files
DbContext/QuoteDbContext.cs CHANGED
@@ -57,6 +57,9 @@ public class Quote
57
  [MaxLength(255)]
58
  public string? Source { get; set; } // Nullable column
59
 
 
 
 
60
  [MaxLength(255)]
61
  public string? Book { get; set; } // Nullable column
62
 
 
57
  [MaxLength(255)]
58
  public string? Source { get; set; } // Nullable column
59
 
60
+ [MaxLength(255)]
61
+ public string DataSet { get; set; }
62
+
63
  [MaxLength(255)]
64
  public string? Book { get; set; } // Nullable column
65
 
GlobalData.cs CHANGED
@@ -1,4 +1,76 @@
1
  public static class GlobalData
2
  {
3
  public static List<Quote> Quotes { get; set; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
 
1
  public static class GlobalData
2
  {
3
  public static List<Quote> Quotes { get; set; }
4
+ }
5
+
6
+ public static class Extensions {
7
+ public static string CleanString(this string str) {
8
+ if (str == null) return "";
9
+ var result = str.Trim().Trim('"');
10
+ result = String.Join(' ', result.Split(' ', StringSplitOptions.RemoveEmptyEntries));
11
+ return result;
12
+ }
13
+
14
+ public static Quote GetQuote1(this MittalInput json) {
15
+ var quote = new Quote();
16
+ quote.QuoteText = json.Content.CleanString();
17
+
18
+ if (json.Author != null) {
19
+ var authorData = json.Author.Split(',');
20
+ quote.Author = authorData[0].CleanString();
21
+ if (authorData.Length > 1){
22
+ quote.Book = authorData[1].CleanString();
23
+ quote.Source = "Book";
24
+ }
25
+ }
26
+ else quote.Author = "Unknown";
27
+
28
+ quote.Language = "english";
29
+
30
+ quote.Categories = String.Join(",", json.Tags.Select(x => x.ToLower().CleanString()).Distinct());
31
+ quote.DataSet = "mittal";
32
+ return quote;
33
+ }
34
+
35
+ public static Quote GetQuote2(this QuotableInput json) {
36
+ var quote = new Quote();
37
+ quote.QuoteText = json.Content.CleanString();
38
+
39
+ if (json.Author != null) {
40
+ var authorData = json.Author.Split(',');
41
+ quote.Author = authorData[0].CleanString();
42
+ if (authorData.Length > 1){
43
+ quote.Book = authorData[1].CleanString();
44
+ quote.Source = "Book";
45
+ }
46
+ }
47
+ else quote.Author = "Unknown";
48
+
49
+ quote.Language = "english";
50
+
51
+ quote.Categories = String.Join(",", json.Tags.Select(x => x.ToLower().CleanString()).Distinct());
52
+ quote.DataSet = "quotable";
53
+ return quote;
54
+ }
55
+
56
+ public static Quote GetQuote3(this ManannInput data) {
57
+ var quote = new Quote();
58
+ quote.QuoteText = data.quote.CleanString();
59
+
60
+ if (data.author != null) {
61
+ var authorData = data.author.Split(',');
62
+ quote.Author = authorData[0].CleanString();
63
+ if (authorData.Length > 1){
64
+ quote.Book = authorData[1].CleanString();
65
+ quote.Source = "Book";
66
+ }
67
+ }
68
+ else quote.Author = "Unknown";
69
+
70
+ quote.Language = "english";
71
+
72
+ quote.Categories = String.Join(",", data.category.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => x.ToLower().CleanString()).Distinct());
73
+ quote.DataSet = "manann";
74
+ return quote;
75
+ }
76
  }
Program.cs CHANGED
@@ -1,6 +1,10 @@
 
1
  using System.Linq;
2
  using System.Text.Json;
 
 
3
  using Microsoft.EntityFrameworkCore;
 
4
 
5
  var builder = WebApplication.CreateBuilder(args);
6
  builder.WebHost.ConfigureKestrel(serverOptions =>
@@ -37,17 +41,21 @@ const int MaxPageSize = 100;
37
 
38
  app.UseCors();
39
 
40
- app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize = 10) =>
41
  {
 
 
 
42
  if (pageNumber < 1) pageNumber = 1;
43
  if (pageSize < 1) pageSize = 10;
44
  pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize
45
 
46
- // var quotes = await db.Quotes
47
- // .Skip((pageNumber - 1) * pageSize)
48
- // .Take(pageSize)
49
- // .ToListAsync();
50
- var quotes = GlobalData.Quotes
 
51
  .Skip((pageNumber - 1) * pageSize)
52
  .Take(pageSize)
53
  .ToList();
@@ -81,7 +89,7 @@ app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize
81
  // quote.Isbn = updatedQuote.Isbn;
82
  // quote.Language = updatedQuote.Language;
83
  // quote.OriginalLanguage = updatedQuote.OriginalLanguage;
84
-
85
  // await db.SaveChangesAsync();
86
  // return Results.NoContent();
87
  // });
@@ -97,24 +105,56 @@ app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize
97
  // });
98
 
99
  // Random quote endpoint
100
- app.MapGet("/quotes/random", async (QuoteDbContext db) =>
101
  {
102
- var quoteCount = GlobalData.Quotes.Count(); //await db.Quotes.CountAsync();
 
 
 
 
 
 
 
 
 
 
 
 
103
  if (quoteCount == 0)
104
  return Results.NotFound("No quotes available.");
105
 
106
  var random = new Random();
107
  var randomIndex = random.Next(quoteCount);
108
 
109
- var randomQuote = GlobalData.Quotes[randomIndex]; //await db.Quotes.Skip(randomIndex).Take(1).FirstOrDefaultAsync();
 
 
 
 
 
 
 
 
 
 
 
 
110
  return randomQuote != null ? Results.Ok(randomQuote) : Results.NotFound("No quote found.");
111
  });
112
 
113
  // Search quotes by author, categories, book, or quoteText with pagination
114
- app.MapGet("/quotes/search", async (string query, QuoteDbContext db, int pageNumber = 1, int pageSize = 10) =>
115
  {
 
 
 
116
  var queryQuotes = GlobalData.Quotes.AsQueryable(); //db.Quotes.AsQueryable();
117
 
 
 
 
 
 
118
  if (!string.IsNullOrWhiteSpace(query))
119
  {
120
  query = query.ToLower();
@@ -148,14 +188,6 @@ async Task SaveSource1Async(string jsonFilePath, QuoteDbContext db)
148
  if (!File.Exists(path))
149
  throw new FileNotFoundException("The JSON file for seeding is missing.");
150
 
151
- // var jsonString = await File.ReadAllTextAsync(path);
152
-
153
- // // Deserialize JSON to a list of dictionaries
154
- // var dictionaryList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(jsonString);
155
-
156
- // if (dictionaryList == null)
157
- // throw new InvalidOperationException("Failed to deserialize JSON into dictionary list.");
158
-
159
  // Fields
160
  // ==========
161
  // Quote (string)
@@ -163,26 +195,19 @@ async Task SaveSource1Async(string jsonFilePath, QuoteDbContext db)
163
  // Tags (list<string>)
164
  // Category (string)
165
 
166
- // foreach (var dictionary in dictionaryList)
167
- // {
168
- // var tags = "";
169
- // if(dictionary.TryGetValue("Tags", out var categories)) {
170
- // var list = JsonSerializer.Deserialize<List<string>>(categories.ToString());
171
- // tags = String.Join(",", list.Select(x=> x.Trim().Trim('"')));
172
- // }
173
- // var quote = new Quote
174
- // {
175
- // Author = dictionary.TryGetValue("Author", out var author) ? author.ToString() : null,
176
- // QuoteText = dictionary.TryGetValue("Quote", out var quoteText) ? quoteText.ToString() : null,
177
- // Categories = tags,
178
- // };
179
- // db.Quotes.Add(quote);
180
- // }
181
-
182
  // TODO: import data and sanitize relevant fields: Trim and Trim('"')
183
-
184
- //save for each seed file
185
- await db.SaveChangesAsync();
 
 
 
 
 
 
 
 
186
  }
187
 
188
  async Task SaveSource2Async(string jsonFile, QuoteDbContext db)
@@ -198,10 +223,19 @@ async Task SaveSource2Async(string jsonFile, QuoteDbContext db)
198
  // author (string)
199
  // tags (list<string>)
200
 
 
201
  // TODO: import data and sanitize relevant fields: Trim and Trim('"')
202
-
203
- //save for each seed file
204
- await db.SaveChangesAsync();
 
 
 
 
 
 
 
 
205
  }
206
 
207
  async Task SaveSource3Async(string csvFile, QuoteDbContext db)
@@ -217,10 +251,19 @@ async Task SaveSource3Async(string csvFile, QuoteDbContext db)
217
  // author (string)
218
  // category (string)
219
 
220
- // TODO: import data and sanitize relevant fields: Trim and Trim('"')
221
-
222
- //save for each seed file
223
- await db.SaveChangesAsync();
 
 
 
 
 
 
 
 
 
224
  }
225
 
226
  // Seed database
@@ -238,9 +281,9 @@ using (var scope = app.Services.CreateScope())
238
  {
239
  var db = scope.ServiceProvider.GetRequiredService<QuoteDbContext>();
240
  db.Database.EnsureCreated();
241
-
242
  GlobalData.Quotes = await db.Quotes.ToListAsync();
243
- await SeedDatabase(db);
244
  }
245
 
246
  app.Run();
 
1
+ using System.Globalization;
2
  using System.Linq;
3
  using System.Text.Json;
4
+ using CsvHelper;
5
+ using Microsoft.AspNetCore.Mvc;
6
  using Microsoft.EntityFrameworkCore;
7
+ using Microsoft.Extensions.ObjectPool;
8
 
9
  var builder = WebApplication.CreateBuilder(args);
10
  builder.WebHost.ConfigureKestrel(serverOptions =>
 
41
 
42
  app.UseCors();
43
 
44
+ app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize = 10, [FromQuery] DataSetSources dataset = DataSetSources.all) =>
45
  {
46
+ if (!Enum.IsDefined(typeof(DataSetSources), dataset))
47
+ return Results.BadRequest("Invalid dataset.");
48
+
49
  if (pageNumber < 1) pageNumber = 1;
50
  if (pageSize < 1) pageSize = 10;
51
  pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize
52
 
53
+ var quotes = GlobalData.Quotes;
54
+ if (dataset != DataSetSources.all)
55
+ {
56
+ quotes = quotes.Where(x => x.DataSet == dataset.ToString()).ToList();
57
+ }
58
+ quotes = quotes
59
  .Skip((pageNumber - 1) * pageSize)
60
  .Take(pageSize)
61
  .ToList();
 
89
  // quote.Isbn = updatedQuote.Isbn;
90
  // quote.Language = updatedQuote.Language;
91
  // quote.OriginalLanguage = updatedQuote.OriginalLanguage;
92
+
93
  // await db.SaveChangesAsync();
94
  // return Results.NoContent();
95
  // });
 
105
  // });
106
 
107
  // Random quote endpoint
108
+ app.MapGet("/quotes/random", async (QuoteDbContext db, [FromQuery] DataSetSources dataset = DataSetSources.all) =>
109
  {
110
+ if (!Enum.IsDefined(typeof(DataSetSources), dataset))
111
+ return Results.BadRequest("Invalid dataset.");
112
+
113
+ int quoteCount = 0;
114
+ if (dataset != DataSetSources.all)
115
+ {
116
+ quoteCount = GlobalData.Quotes.Count(q => q.DataSet == dataset.ToString());
117
+ }
118
+ else
119
+ {
120
+ quoteCount = GlobalData.Quotes.Count(); //await db.Quotes.CountAsync();
121
+ }
122
+
123
  if (quoteCount == 0)
124
  return Results.NotFound("No quotes available.");
125
 
126
  var random = new Random();
127
  var randomIndex = random.Next(quoteCount);
128
 
129
+ Quote? randomQuote = null;
130
+ if (dataset != DataSetSources.all)
131
+ {
132
+ randomQuote = GlobalData.Quotes
133
+ .Where(q => q.DataSet == dataset.ToString())
134
+ .Skip(randomIndex)
135
+ .Take(1)
136
+ .FirstOrDefault();
137
+ }
138
+ else
139
+ {
140
+ randomQuote = GlobalData.Quotes[randomIndex]; //await db.Quotes.Skip(randomIndex).Take(1).FirstOrDefaultAsync();
141
+ }
142
  return randomQuote != null ? Results.Ok(randomQuote) : Results.NotFound("No quote found.");
143
  });
144
 
145
  // Search quotes by author, categories, book, or quoteText with pagination
146
+ app.MapGet("/quotes/search", async (string query, QuoteDbContext db, int pageNumber = 1, int pageSize = 10, [FromQuery] DataSetSources dataset = DataSetSources.all) =>
147
  {
148
+ if (!Enum.IsDefined(typeof(DataSetSources), dataset))
149
+ return Results.BadRequest("Invalid dataset.");
150
+
151
  var queryQuotes = GlobalData.Quotes.AsQueryable(); //db.Quotes.AsQueryable();
152
 
153
+ if (dataset != DataSetSources.all)
154
+ {
155
+ queryQuotes = queryQuotes.Where(q => q.DataSet == dataset.ToString());
156
+ }
157
+
158
  if (!string.IsNullOrWhiteSpace(query))
159
  {
160
  query = query.ToLower();
 
188
  if (!File.Exists(path))
189
  throw new FileNotFoundException("The JSON file for seeding is missing.");
190
 
 
 
 
 
 
 
 
 
191
  // Fields
192
  // ==========
193
  // Quote (string)
 
195
  // Tags (list<string>)
196
  // Category (string)
197
 
198
+ var jsonString = await File.ReadAllTextAsync(path);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  // TODO: import data and sanitize relevant fields: Trim and Trim('"')
200
+ var quotes = JsonSerializer.Deserialize<List<MittalInput>>(jsonString);
201
+ var uniqueQuotes = quotes.DistinctBy(x => x.Content).ToList();
202
+ foreach (var quote in uniqueQuotes)
203
+ {
204
+ var q = quote.GetQuote1();
205
+ if (!db.Quotes.Any(x => x.QuoteText == q.QuoteText.CleanString()))
206
+ {
207
+ db.Quotes.Add(q);
208
+ await db.SaveChangesAsync();
209
+ }
210
+ }
211
  }
212
 
213
  async Task SaveSource2Async(string jsonFile, QuoteDbContext db)
 
223
  // author (string)
224
  // tags (list<string>)
225
 
226
+ var jsonString = await File.ReadAllTextAsync(path);
227
  // TODO: import data and sanitize relevant fields: Trim and Trim('"')
228
+ var quotes = JsonSerializer.Deserialize<List<QuotableInput>>(jsonString);
229
+ var uniqueQuotes = quotes.DistinctBy(x => x.Content).ToList();
230
+ foreach (var quote in uniqueQuotes)
231
+ {
232
+ var q = quote.GetQuote2();
233
+ if (!db.Quotes.Any(x => x.QuoteText == q.QuoteText.CleanString()))
234
+ {
235
+ db.Quotes.Add(q);
236
+ await db.SaveChangesAsync();
237
+ }
238
+ }
239
  }
240
 
241
  async Task SaveSource3Async(string csvFile, QuoteDbContext db)
 
251
  // author (string)
252
  // category (string)
253
 
254
+ //read csv file into list of records
255
+ var reader = new StreamReader(path);
256
+ var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
257
+ var records = csv.GetRecords<ManannInput>().ToList();
258
+ foreach (var record in records)
259
+ {
260
+ var q = record.GetQuote3();
261
+ if (!db.Quotes.Any(x => x.QuoteText == q.QuoteText.CleanString()))
262
+ {
263
+ db.Quotes.Add(q);
264
+ await db.SaveChangesAsync();
265
+ }
266
+ }
267
  }
268
 
269
  // Seed database
 
281
  {
282
  var db = scope.ServiceProvider.GetRequiredService<QuoteDbContext>();
283
  db.Database.EnsureCreated();
284
+
285
  GlobalData.Quotes = await db.Quotes.ToListAsync();
286
+ //await SeedDatabase(db);
287
  }
288
 
289
  app.Run();
QuotesAPI.csproj CHANGED
@@ -10,6 +10,7 @@
10
  </PropertyGroup>
11
 
12
  <ItemGroup>
 
13
  <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
14
  <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
15
  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
 
10
  </PropertyGroup>
11
 
12
  <ItemGroup>
13
+ <PackageReference Include="CsvHelper" Version="33.0.1" />
14
  <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
15
  <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
16
  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Quotes Fastapi
3
  emoji: 🐨
4
  colorFrom: yellow
5
  colorTo: yellow
@@ -17,9 +17,9 @@ Welcome to the **Quotes API**, your go-to source for inspirational, motivational
17
  Our API draws from an extensive database of more than half a million quotes to keep you inspired every single day.
18
  - **Diverse Sources**
19
  We've combined quotes from various curated datasets:
20
- - [Manann’s 500k Quotes](https://www.kaggle.com/datasets/manann/quotes-500k) - 463,402 unique quotes 📝
21
  - [AK Mittal's Quotes Collection](https://www.kaggle.com/datasets/akmittal/quotes-dataset) - 36,937 unique quotes 📝
22
  - [Quotable’s Quote Collection](https://github.com/quotable-io/data/blob/master/data/quotes.json) - 1,634 unique quotes 📝
 
23
  - **Easy Integration**
24
  Plug it into your app, website, or daily routine with ease. Just call the API and get inspired instantly! 🚀
25
 
 
1
  ---
2
+ title: Quotes
3
  emoji: 🐨
4
  colorFrom: yellow
5
  colorTo: yellow
 
17
  Our API draws from an extensive database of more than half a million quotes to keep you inspired every single day.
18
  - **Diverse Sources**
19
  We've combined quotes from various curated datasets:
 
20
  - [AK Mittal's Quotes Collection](https://www.kaggle.com/datasets/akmittal/quotes-dataset) - 36,937 unique quotes 📝
21
  - [Quotable’s Quote Collection](https://github.com/quotable-io/data/blob/master/data/quotes.json) - 1,634 unique quotes 📝
22
+ - [Manann’s 500k Quotes](https://www.kaggle.com/datasets/manann/quotes-500k) - 463,430 unique quotes 📝
23
  - **Easy Integration**
24
  Plug it into your app, website, or daily routine with ease. Just call the API and get inspired instantly! 🚀
25
 
models/DataSetSources.cs ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Text.Json.Serialization;
2
+
3
+ [JsonConverter(typeof(JsonStringEnumConverter))]
4
+ public enum DataSetSources
5
+ {
6
+ all,
7
+ mittal,
8
+ quotable,
9
+ manann
10
+ }
models/ManannInput.cs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ using System.Text.Json.Serialization;
2
+
3
+ public class ManannInput
4
+ {
5
+ public string? author { get; set; }
6
+ public string? quote { get; set; }
7
+ public string? category { get; set; }
8
+ }
models/MittalInput.cs ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Text.Json.Serialization;
2
+
3
+ public class MittalInput
4
+ {
5
+ [JsonPropertyName("Author")]
6
+ public string? Author { get; set; }
7
+ [JsonPropertyName("Quote")]
8
+ public string? Content { get; set; }
9
+ [JsonPropertyName("Tags")]
10
+ public List<string>? Tags { get; set; }
11
+ }
models/QuotableInput.cs ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Text.Json.Serialization;
2
+
3
+ public class QuotableInput
4
+ {
5
+ [JsonPropertyName("author")]
6
+ public string? Author { get; set; }
7
+ [JsonPropertyName("content")]
8
+ public string? Content { get; set; }
9
+ [JsonPropertyName("tags")]
10
+ public List<string>? Tags { get; set; }
11
+ }
quotes.db CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:83243ae11dc6227399713a634cc278f21f7463cf9460a43272cc7e40998bdb47
3
- size 350629888
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5a3df3c597a721719176229988f30d2e3eb033ab869ccde1cb2a59a10338ecb
3
+ size 347443200