How to Upload a list of objects to Firestore - firebase

I'm developing flutter app with FireStore.
I have declared a variable 'total' and List of objects to be filled with some data from collection called 'cart', then upload these data to collection called 'orders'
This code grab the data from collection 'cart':
void submitOrder() async {
List<CartItem> products =[];
double total =0;
CartItem temp= CartItem(customerId:'' ,itemId:'' ,id: '', title: '', quantity:0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()) ,
price: double.tryParse( doc.data['price'].toString()));
total += temp.quantity*temp.price;
print(total); /// This print shows accurate total
products.add(temp);
}
),
);
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
}
method setOrderData in DB class:
// Add Order
Future setOrderData(List<CartItem> cartProducts, double total, String customerId, String branchId, String status ) async {
final timestamp = DateTime.now();
return await orderCollection.document(uid).setData(
{
'customerId': customerId,
'branchId': branchId,
'status': status ,
'amount': total.toString(), //
'dateTime': timestamp.toIso8601String(),
'products': cartProducts.map((cp) => {
'id': cp.id,
'title': cp.title,
'quantity': cp.quantity,
'price': cp.price,
}).toList(),
});
}
data in FireStore shows that products and total are null?
Link to image:
If anyone can help me out I'd be grateful.

I highly recommend using a code formatter on your code, as it makes it much more likely that people can spot problems like the one you're having. When I reformat the first code block you shared, it becomes:
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
);
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
}
An in this format it's immediately clear to me that you're calling setOrderData before any of the products.add(temp) calls have happened.
This is because data is loaded from Firestore asynchronously. Since this may take some time, your main code (including the return) continues so the UI is not blocked. Then when the data is available, your then callback is invoked.
This means that any code that needs the data from Firestore needs to be inside the then callback. So in your case, the solution could be as simple as moving the call to setOrderData to inside the then:
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
);
}
So the things to take away from this:
Always format your code, as it makes it easier for you and others to understand the flow and find problems.
Data is loaded from Firestore (and most cloud APIs) asynchronously, and you can only use the data inside the then() callback, or by using await.

The best Practice to solve such an error is to use try and then catch with the await for each function like the below code
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
try {
await DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
);
// Send data to setOrderData in db class to set new doc in order collection
await DatabaseService()
.setOrderData(products, total, user.uid, branchId, 'open');
} catch (error) {
print(error);
}
}

Related

How do I scrape all spotify playlists ever? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 days ago.
This post was edited and submitted for review 5 days ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
I am trying to analyze all user-curated Spotify playlists and the tracks inside all of them, especially in the hip-hop genre.
I have tried using search API and Get Category’s Playlist Spotify API but there are limits around 1000 data points.
I am trying to go around the API by thinking of parsing different queries, but still have no idea which query can give me all data. I would appreciate any help!
I am expecting a list of all user-curated Spotify playlist IDs.
This is what I have tried with Get Category’s Playlist Spotify API with Spotipy Library in Google Colab
import pandas as pd
import numpy as np
import spotipy
import spotipy.util as util
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy.oauth2 as oauth2
# Replace Auth details with your Client ID, Secret
spotify_details = {
'client_id' : 'Client ID',
'client_secret':'Client Secret',
'redirect_uri':'Redirect_uri'}
scope = "user-library-read user-follow-read user-top-read playlist-read-private playlist-read-collaborative playlist-modify-public playlist-modify-private"
sp = spotipy.Spotify(
auth_manager=spotipy.SpotifyOAuth(
client_id=spotify_details['client_id'],
client_secret=spotify_details['client_secret'],
redirect_uri=spotify_details['redirect_uri'],
scope=scope,open_browser=False))
results = sp.category_playlists(category_id="hiphop", limit = 5, country="US", offset=0)
total = results["playlists"]["total"]
df=pd.DataFrame([],columns = ['id', 'name', 'external_urls.spotify'])
for offset in range(0,total,50):
results = sp.category_playlists(category_id="hiphop", limit = 50, country="US", offset=offset)
playlists = pd.json_normalize(results['playlists']['items'])
#print(playlists.keys)
df=pd.concat([df,playlists])
df
I only can get around 104 playlists when I run
print(len(df))
>>104
P.S. This number varies around 80-100+ depending on the location of your account.
Main idea is same as #Nima Akbarzadeh's idea with offset
I am using axios call with Spotify API call on node.js
Got the playlists first, then get track within loop each playlist.
This Code can get all of hiphop songs from Spotify.
const axios = require('axios')
const API_KEY='<your client ID>'
const API_KEY_SECRET='<your client Secret>'
const getToken = async () => {
try {
const resp = await axios.post(
url = 'https://accounts.spotify.com/api/token',
data = '',
config = {
params: {
'grant_type': 'client_credentials'
},
auth: {
username: API_KEY,
password: API_KEY_SECRET
}
}
);
return Promise.resolve(resp.data.access_token);
} catch (err) {
console.error(err)
return Promise.reject(err)
}
};
const getCategories = async (category_id, token) => {
try {
let offset = 0
let next = 1
const songs = [];
while (next != null) {
const resp = await axios.get(
url = `https://api.spotify.com/v1/browse/categories/${category_id}/playlists?country=US&offset=${offset}&limit=20`,
config = {
headers: {
'Accept-Encoding': 'application/json',
'Authorization': `Bearer ${token}`,
}
}
);
for(const item of resp.data.playlists.items) {
if(item?.name != null) {
songs.push({
name: item.name,
external_urls: item.external_urls.spotify,
type: item.type,
id : item.id
})
}
}
offset = offset + 20
next = resp.data.playlists.next
}
return Promise.resolve(songs)
} catch (err) {
console.error(err)
return Promise.reject(err)
}
}
const getTracks = async (playlists, token) => {
try {
const tracks = [];
for(const playlist of playlists) {
const resp = await axios.get(
url = `https://api.spotify.com/v1/playlists/${playlist.id}`,
config = {
headers: {
'Accept-Encoding': 'application/json',
'Authorization': `Bearer ${token}`,
}
}
);
for(const item of resp.data.tracks.items) {
if(item.track?.name != null) {
tracks.push({
name: item.track.name,
external_urls: item.track.external_urls.spotify
})
}
}
}
return Promise.resolve(tracks)
} catch (err) {
console.error(err)
return Promise.reject(err)
}
};
getToken()
.then(token => {
getCategories('hiphop', token)
.then(playlists => {
getTracks(playlists, token)
.then(tracks => {
for(const track of tracks) {
console.log(track)
}
})
.catch(error => {
console.log(error.message);
});
})
.catch(error => {
console.log(error.message);
});
})
.catch(error => {
console.log(error.message);
});
I got 6435 songs
$ node get-data.js
[
{
name: 'RapCaviar',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd',
type: 'playlist',
id: '37i9dQZF1DX0XUsuxWHRQd'
},
{
name: "Feelin' Myself",
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DX6GwdWRQMQpq',
type: 'playlist',
id: '37i9dQZF1DX6GwdWRQMQpq'
},
{
name: 'Most Necessary',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DX2RxBh64BHjQ',
type: 'playlist',
id: '37i9dQZF1DX2RxBh64BHjQ'
},
{
name: 'Gold School',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DWVA1Gq4XHa6U',
type: 'playlist',
id: '37i9dQZF1DWVA1Gq4XHa6U'
},
{
name: 'Locked In',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DWTl4y3vgJOXW',
type: 'playlist',
id: '37i9dQZF1DWTl4y3vgJOXW'
},
{
name: 'Taste',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DWSUur0QPPsOn',
type: 'playlist',
id: '37i9dQZF1DWSUur0QPPsOn'
},
{
name: 'Get Turnt',
external_urls: 'https://open.spotify.com/playlist/37i9dQZF1DWY4xHQp97fN6',
type: 'playlist',
id: '37i9dQZF1DWY4xHQp97fN6'
},
...
{
name: 'BILLS PAID (feat. Latto & City Girls)',
external_urls: 'https://open.spotify.com/track/0JiLQRLOeWQdPC9rVpOqqo'
},
{
name: 'Persuasive (with SZA)',
external_urls: 'https://open.spotify.com/track/67v2UHujFruxWrDmjPYxD6'
},
{
name: 'Shirt',
external_urls: 'https://open.spotify.com/track/34ZAzO78a5DAVNrYIGWcPm'
},
{
name: 'Back 2 the Streets',
external_urls: 'https://open.spotify.com/track/3Z9aukqdW2HuzFF1x9lKUm'
},
{
name: 'FTCU (feat. GloRilla & Gangsta Boo)',
external_urls: 'https://open.spotify.com/track/4lxTmHPgoRWwM9QisWobJL'
},
{
name: 'My Way',
external_urls: 'https://open.spotify.com/track/5BcIBbBdkjSYnf5jNlLG7j'
},
{
name: 'Donk',
external_urls: 'https://open.spotify.com/track/58lmOL5ql1YIXrpRpoYi3i'
},
... 6335 more items
]
node get-data.js > result.json
Update with Python version
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import json
import re
SCOPE = ['user-library-read',
'user-follow-read',
'user-top-read',
'playlist-read-private',
'playlist-read-collaborative',
'playlist-modify-public',
'playlist-modify-private']
USER_ID = '<your user id>'
REDIRECT_URI = '<your redirect uri>'
CLIENT_ID = '<your client id>'
CLIENT_SECRET = '<your client secret>'
auth_manager = SpotifyOAuth(
scope=SCOPE,
username=USER_ID,
redirect_uri=REDIRECT_URI,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET)
def get_categories():
try:
sp = spotipy.Spotify(auth_manager=auth_manager)
query_limit = 50
categories=[]
new_offset = 0
while True:
results=sp.category_playlists(category_id='hiphop', limit = query_limit, country='US', offset=new_offset)
for item in results['playlists']['items']:
if (item is not None and item['name'] is not None):
# ['https:', '', 'api.spotify.com', 'v1', 'playlists', '37i9dQZF1DX0XUsuxWHRQd', 'tracks']
tokens = re.split(r"[\/]", item['tracks']['href'])
categories.append({
'id' : item['id'],
'name': item['name'],
'url': item['external_urls']['spotify'],
'tracks': item['tracks']['href'],
'playlist_id': tokens[5],
'type': item['type']
})
new_offset = new_offset + query_limit
next = results['playlists']['next']
if next is None:
break
return categories
except Exception as e:
print('Failed to upload to call get_categories: '+ str(e))
def get_songs(categories):
try:
sp = spotipy.Spotify(auth_manager=auth_manager)
songs=[]
for category in categories:
if category is None:
break
playlist_id = category['playlist_id']
results=sp.playlist(playlist_id=playlist_id)
for item in results['tracks']['items']:
if (item is not None and item['track'] is not None and item['track']['id'] is not None and item['track']['name'] is not None and item['track']['external_urls']['spotify'] is not None):
songs.append({
'id' : item['track']['id'],
'name': item['track']['name'],
'url': item['track']['external_urls']['spotify']
})
else:
break
return songs
except Exception as e:
print('Failed to upload to call get_songs: '+ str(e))
categories = get_categories()
songs = get_songs(categories)
print(json.dumps(songs))
# print(len(songs)) -> 6021
Result by
$ python get-songs.py > all-songs.json
Currently, Spotify will not let you scrape more than 1K as their application even show maximum 1k music (based on this answer).
Also, if there is any offset option, you can set it to 1k, and it will skip the first 1k, so you can get the second chunk.

how can I change all cached query data from only a user_id RTK QUERY

I have a problem.
I fetch data with 2 parameters.
user_id and movie_channel
so a user has multiple movie channels like 1,2 or 3.
I fetch now a query with this params:
user_id: 1, movie_channel: 1
obj:
return {
user: {
user_id: 1,
username: 'assa',
is_follow: false
},
movie_channel: 1,
movies: []
}
then I get a list of movies from this channel and you get users information.
So anyone select now movie_channel 2, then I fetch again and get the obj with different movies.
in the header he can follow a person. (he is current now in movie channel 2)
he can now change the movie_channel to 1 and then I get the cached data. But now user is not followed because he followed in the channel 2. the cache shows the old obj.
how can I change all cached data where only the param is user_id ?
useGetProfileData: builder.query<IProfilePageData, { user_id: number; movie_channel?: number; }>({
query: (data) => ({
url: '/profile_data',
method: 'POST',
body: data
}),
}),
followUser: builder.mutation<void, { user_id: number; follower_id: number; movie_channel?: number; }>({
query: (data) => ({
url: '/follow_user',
method: 'POST',
body: data
}),
async onQueryStarted({ user_id, follower_id, movie_channel }, { dispatch, queryFulfilled }){
const patchResult = dispatch(
ProfileApi.util.updateQueryData('useGetProfileData', { user_id, movie_channel }, (draft) => {
return {
...draft,
user: {
...draft.user,
is_follow: !draft.user.is_follow
}
}
})
);
try {
await queryFulfilled;
} catch {
patchResult.undo();
}
}
}),

Update Firebase with the final score and display to user

I'm getting the score and topicTotal from the state and printing them, however, I want to update Firebase with the topicTotal by retrieving topicTotal in report. Every way I try to access topicTotal in report, I get null in Firebase or The getter 'topicTotal' was called on null.
How can I access topicTotal in report and update Firebase with it? Also, how can I display topicTotal to the user?
set score(Options newValue) {
var score = idx += newValue.score;
_score = newValue;
print(score);
_score = newValue;
notifyListeners();
}
set topicTotal(Options newValue) {
var topicTotal = idx;
_score = newValue;
this._topicTotal = newValue;
print(topicTotal);
notifyListeners();
}
model
class Report {
...
int topicTotal;
Report({ this.uid, this.topics, this.email, this.total, this.topicTotal, this.level, this.id, this.title, this.description, this.img });
factory Report.fromMap(Map data) {
return Report(
uid: data['uid'],
email: data['email'],
total: data['total'] ?? 0,
topics: data['topics'] ?? {},
topicTotal: data['topicTotal'] ?? 34,
level: data['level'] ?? 383,
id: data['id'] ?? '',
title: data['title'] ?? '',
description: data['description'] ?? '',
img: data['img'] ?? 'default.png',
);
}
}
Here I can access total but not topicTotal
if (report != null)
Text('${report.total ?? 0}',
style: Theme.of(context).textTheme.display3),
Text('Assessments Completed',
style: Theme.of(context).textTheme.subhead),
Spacer(),
if (report != null)
Text('your topic score is ${report.topicTotal ?? 0}'),
onPressed: () {
_updateUserReportWithTopics(assessment, state, optionSelected);
Future<void> _updateUserReportWithTopics(Assessment assessment, AssessmentState state, Options optionSelected) {
state.topicTotal = optionSelected;
return Global.reportRef.upsert(
({
'total': FieldValue.increment(1),
'topics': {
'${assessment.topic}': FieldValue.arrayUnion([
assessment.title,
assessment.topicTotal,
assessment.questions.length,
]),
},
//'topicTotal': state.topicTotal = optionSelected
}),
);
}

How can I upload an image to firebase storage and add it to the database?

I'm new to Vuejs. I want to have a form using which you can add products. The product image goes to firebase storage but how do I associate that image with the exact product in the database?
I've already set up my form, and created two methods. saveProduct() to save the products to the database and onFilePicked() to listen for changes in the input field and target the image and upload that to storage.
import { fb, db } from '../firebaseinit'
export default {
name: 'addProduct',
data () {
return {
product_id: null,
name: null,
desc: null,
category: null,
brand: null,
image: null,
}
},
methods: {
saveProduct () {
db.collection('products').add({
product_id: this.product_id,
name: this.name,
desc: this.desc,
category: this.category,
brand: this.brand
})
.then(docRef => {
this.$router.push('/fsbo/produkten')
})
},
onFilePicked (event) {
let imageFile = event.target.files[0]
let storageRef = fb.storage().ref('products/' + imageFile.name)
storageRef.put(imageFile)
}
}
}
what about this, you can use the filename, your images are going to be served as somefireurl.com/{your_file_name} on your product collection you can have an image prop with the imageFile.name.
methods: {
saveProduct (image = null) {
let productRef = db.collection('products').doc(this.product_id)
const payload = {
product_id: this.product_id,
name: this.name,
desc: this.desc,
category: this.category,
brand: this.brand
}
if (image) payload['image'] = image
return productRef
.set(payload, {merge: true})
.then(docRef => {
this.$router.push('/fsbo/produkten')
})
},
onFilePicked (event) {
let imageFile = event.target.files[0]
let storageRef = fb.storage().ref('products/' + imageFile.name)
storageRef.put(imageFile)
return this.saveProduct(imageFile.name)
}
}
That should be enough to get you started, maybe you want to try a different combination, or maybe you dont want to call saveProduct the way I set it, it's up to your use case but the idea is the same. Hope this can help you
I fixed it myself. Here's my solution. I don't know if it's technically correct but it works for my use case.
methods: {
saveProduct () {
let imageFile
let imageFileName
let ext
let imageUrl
let key
let task
db.collection('products').add({
product_id: this.product_id,
name: this.name,
desc: this.desc,
category: this.category,
brand: this.brand
})
.then(docRef => {
key = docRef.id
this.$router.push('/fsbo/produkten')
return key
})
.then(key => {
if(this.image !== null) {
this.onFilePicked
imageFile = this.image
imageFileName = imageFile.name
ext = imageFileName.slice(imageFileName.lastIndexOf('.'))
}
let storageRef = fb.storage().ref('products/' + key + '.' + ext)
let uploadTask = storageRef.put(imageFile)
uploadTask.on('state_changed', (snapshot) => {}, (error) => {
// Handle unsuccessful uploads
}, () => {
uploadTask.snapshot.ref.getDownloadURL().then( (downloadURL) => {
db.collection('products').doc(key).update({ imageUrl: downloadURL})
});
});
})
},
onFilePicked (event) {
return this.image = event.target.files[0]
}
}

How would you find a collection dynamically in Meteor with a value?

Given I have 3 types of collections and a dynamic value, how would I specify what collection to search for based on that dynamic value?
E.g,
array = [
{id: 'one', type: 'profile'},
{id: 'something', type: 'post'},
{id: 'askjdaksj', type: 'comment']
]
How would I isolate the type and turn it into a collection? Basically turning type into Collection.find
array[0].type.find({_id: id});
=> Profiles.find({_id: id});
Is this possible?
Here's a complete working example:
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');
var capitalize = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
var nameToCollection = function(name) {
// pluralize and capitalize name, then find it on the global object
// 'post' -> global['Posts'] (server)
// 'post' -> window['Posts'] (client)
return this[capitalize(name) + 's'];
};
Meteor.startup(function() {
// ensure all old documents are removed
Posts.remove({});
Comments.remove({});
// insert some new documents
var pid = Posts.insert({text: 'I am a post'});
var cid = Comments.insert({text: 'I am a comment'});
var items = [
{id: pid, type: 'post'},
{id: cid, type: 'comment'}
];
_.each(items, function(item) {
// find the collection object based on the type (name)
var collection = nameToCollection(item.type);
// retrieve the document from the dynamically found collection
var doc = collection.findOne(item.id);
console.log(doc);
});
});
Recommended reading: collections by reference.

Resources