SMove

Scrum es una metodología de trabajo que se usa para resolver proyectos, especialmente proyectos de desarrollo de software, en este foro se resolveran dudas sobre SCRUM
User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Sprint #10

Post by JuanG0617 » Mon Apr 01, 2019 5:28 pm

Fecha de inicio: 1-04-2019
Fecha fin: 8-04-2019

Actividades

- Conectar Firebase con el proyecto. (10)
- Corregir el archivo readme.md. (1)

¿Qué hice hasta hoy?

- Conectar Firebase con el proyecto.
Cree el proyecto en firebase.google.com, agregué las apps para Android y iOS
Image

Luego, mientras registraba la app para Android tuve que buscar el certificado SHA-1 por lo que seguí el tutorial que me indicaban en la pagina para conseguirlo.
Por ultimo, cree los archivos e instalé los NuGet's necesarios para poder conectarse a la base en los diferentes proyectos de Android y iOS
Cree dos interfaces comunes para poder utilizarlas en ambos proyectos:

IFirebaseAuthService.cs

Code: Select all

namespace SMove.Services
{
    using System;
    using System.Threading.Tasks;

    public interface IFirebaseAuthService
    {
        String getAuthKey();
        bool IsUserSigned();
        Task<bool> SignUp(String email, String password);
        Task<bool> SignIn(String email, String password);
        void SignInWithGoogle();
        Task<bool> SignInWithGoogle(String token);
        Task<bool> Logout();
        String GetUserId();
    }
}
Y IFirebaseDBService.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Text;

namespace SMove.Services
{
    public interface IFirebaseDBService
    {
        void Connect();
        void GetMessage();
        void SetMessage(String message);
        string GetMessageKey();
        void DeleteItem(string key);
    }
}
Para Android, instalé Xamarin.GooglePlayServices.Auth y Xamarin.Firebase.Database, al igual que Firebase–Auth. Cree los sigueintes archivos:

FirebaseAuthService.cs

Code: Select all

namespace SMove.Droid
{
    using System;
    using System.Threading.Tasks;
    using Android.App;
    using Android.Content;
    using Firebase.Auth;
    using SMove.Services;
    using Xamarin.Forms;

    public class FirebaseAuthService : IFirebaseAuthService
    {
        public static int REQ_AUTH = 9999;
        public static string KEY_AUTH = "auth";

        public string getAuthKey()
        {
            return KEY_AUTH;
        }

        public bool IsUserSigned()
        {
            var user = Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app).CurrentUser;
            var signedIn = user != null;
            return signedIn;
        }

        public async Task<bool> Logout()
        {
            try
            {
                Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app).SignOut();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public async Task<bool> SignIn(string email, string password)
        {
            try
            {
                await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app).SignInWithEmailAndPasswordAsync(email, password);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public void SignInWithGoogle()
        {
            var googleIntent = new Intent(Forms.Context, typeof(GoogleLoginActivity));
            ((Activity)Forms.Context).StartActivityForResult(googleIntent, REQ_AUTH);
        }

        public async Task<bool> SignInWithGoogle(string token)
        {
            try
            {
                AuthCredential credential = GoogleAuthProvider.GetCredential(token, null);
                await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app).SignInWithCredentialAsync(credential);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public async Task<bool> SignUp(string email, string password)
        {
            try
            {
                await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app).CreateUserWithEmailAndPasswordAsync(email, password);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public string GetUserId()
        {
            var user = Firebase.Auth.FirebaseAuth.GetInstance

            (MainActivity.app).CurrentUser;
            return user.Uid;
        }
    }
}
FirebaseDBService.cs

Code: Select all

using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Firebase.Auth;
using Android.App;
using Android.Content;
using Firebase.Database;
using SMove.Droid.Servicios;
using System.Collections.ObjectModel;
using System.Linq;
using SMove.Services;

[assembly: Dependency(typeof(FirebaseDBService))]
namespace SMove.Droid.Servicios
{
    public class ValueEventListener : Java.Lang.Object, IValueEventListener
    {
        public void OnCancelled(DatabaseError error) { }

        public void OnDataChange(DataSnapshot snapshot)
        {
            String message = snapshot.Value.ToString();
            MessagingCenter.Send(FirebaseDBService.KEY_MESSAGE, FirebaseDBService.KEY_MESSAGE, message);
        }
    }

    public class FirebaseDBService : IFirebaseDBService
    {
        DatabaseReference databaseReference;
        FirebaseDatabase database;
        FirebaseAuthService authService = new FirebaseAuthService();
        public static String KEY_MESSAGE = "items";

        public void Connect()
        {
            database = FirebaseDatabase.GetInstance(MainActivity.app);
        }

        public void GetMessage()
        {
            var userId = authService.GetUserId();
            databaseReference = database.GetReference("items/" + userId);
            databaseReference.AddValueEventListener(new ValueEventListener());
        }

        public string GetMessageKey()
        {
            return KEY_MESSAGE;
        }

        public void SetMessage(string message)
        {
            var userId = authService.GetUserId();
            databaseReference = database.GetReference("items/" + userId);

            String key = databaseReference.Push().Key;

            databaseReference.Child(key).SetValue(message);
        }

        public void DeleteItem(string key)
        {
            var userId = authService.GetUserId();
            databaseReference = database.GetReference("items/" + userId);
            databaseReference.Child(key).RemoveValue();
        }
    }
}
Y GoogleLoginActivity.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V7.App;
using Android.Gms.Common.Apis;
using Android.Gms.Common;
using System.Threading.Tasks;
using Android.Gms.Auth.Api.SignIn;
using Android.Gms.Auth.Api;
using Firebase.Auth;

namespace SMove.Droid
{
    [Activity(Label = "GoogleLogin", Theme = "@style/Theme.AppCompat.Light.DarkActionBar")]
    public class GoogleLoginActivity : AppCompatActivity, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
    {
        const string TAG = "GoogleLoginActivity";

        const int RC_SIGN_IN = 9001;

        const string KEY_IS_RESOLVING = "is_resolving";
        const string KEY_SHOULD_RESOLVE = "should_resolve";

        static GoogleApiClient mGoogleApiClient;

        bool mIsResolving = false;

        bool mShouldResolve = false;

        private static GoogleSignInAccount mAuth;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            String token = "745578531830-t2k4mc0gepe64h0ad6iolo2svgjaoa7n.apps.googleusercontent.com";
            GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn).RequestIdToken(token).Build();

            mGoogleApiClient = new GoogleApiClient.Builder(this)
            .AddConnectionCallbacks(this)
            .AddOnConnectionFailedListener(this)
            .AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .Build();

            Intent signInIntent = Auth.GoogleSignInApi.GetSignInIntent(mGoogleApiClient);
            StartActivityForResult(signInIntent, RC_SIGN_IN);
        }

        private void HandleResult(GoogleSignInAccount result)
        {
            if (result != null)
            {
                Intent myIntent = new Intent(this, typeof(GoogleLoginActivity));
                myIntent.PutExtra("result", result);
                SetResult(Result.Ok, myIntent);
            }
            Finish();
        }

        private async void UpdateData(bool isSignedIn)
        {
            if (isSignedIn)
            {
                HandleResult(mAuth);
            }
            else
            {
                await System.Threading.Tasks.Task.Delay(2000);
                mShouldResolve = true;
                mGoogleApiClient.Connect();
            }
        }

        protected override void OnStart()
        {
            base.OnStart();
            mGoogleApiClient.Connect();
        }

        protected override void OnStop()
        {
            base.OnStop();
            mGoogleApiClient.Disconnect();
        }

        protected override void OnSaveInstanceState(Bundle outState)
        {
            base.OnSaveInstanceState(outState);
            outState.PutBoolean(KEY_IS_RESOLVING, mIsResolving);
            outState.PutBoolean(KEY_SHOULD_RESOLVE, mIsResolving);
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            if (requestCode == RC_SIGN_IN)
            {
                var result = Android.Gms.Auth.Api.Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
                if (result.IsSuccess)
                {
                    // Google Sign In was successful, authenticate with Firebase
                    HandleResult(result.SignInAccount);

                }
                else
                {
                    // Google Sign In failed, update UI appropriately
                    HandleResult(null);
                }
            }
        }

        public void OnConnected(Bundle connectionHint)
        {
            UpdateData(false);
        }

        public void OnConnectionSuspended(int cause) { }

        public void OnConnectionFailed(ConnectionResult result)
        {
            if (!mIsResolving && mShouldResolve)
            {
                if (result.HasResolution)
                {
                    try
                    {
                        result.StartResolutionForResult(this, RC_SIGN_IN);
                        mIsResolving = true;
                    }
                    catch (IntentSender.SendIntentException e)
                    {
                        mIsResolving = false;
                        mGoogleApiClient.Connect();
                    }
                }
                else
                {
                    ShowErrorDialog(result);
                }
            }
            else
            {
                UpdateData(false);
            }
        }

        class DialogInterfaceOnCancelListener : Java.Lang.Object, IDialogInterfaceOnCancelListener
        {
            public Action<IDialogInterface> OnCancelImpl { get; set; }

            public void OnCancel(IDialogInterface dialog)
            {
                OnCancelImpl(dialog);
            }
        }

        void ShowErrorDialog(ConnectionResult connectionResult)
        {
            int errorCode = connectionResult.ErrorCode;

            if (GoogleApiAvailability.Instance.IsUserResolvableError(errorCode))
            {

                var listener = new DialogInterfaceOnCancelListener();
                listener.OnCancelImpl = (dialog) =>
                {

                    mShouldResolve = false;
                };
                GoogleApiAvailability.Instance.GetErrorDialog(this, errorCode, RC_SIGN_IN, listener).Show();
            }
            else
            {
                mShouldResolve = false;
            }
            HandleResult(mAuth);
        }
    }
}
Para iOS, instalé los NuGet's de Xamarin.Firebase.iOs.Auth, Xamarin.Auth y Xamarin.Firebase.iOS.Database. Se crearon los siguientes archivos:

FirebaseAuthService.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Firebase.Auth;
using Foundation;
using SMove.Services;
using UIKit;
using Xamarin.Auth;
using Xamarin.Forms;
using SMove.iOS.Servicios;

[assembly: Dependency(typeof(FirebaseDBService))]
namespace SMove.iOS
{
    public class FirebaseAuthService : IFirebaseAuthService
    {
        public static String KEY_AUTH = "auth";
        public static OAuth2Authenticator XAuth;
        private static bool hasLoginResult = false;
        private static bool loginResult = false;
        private static bool signUpResult = false;
        CancellationTokenSource tokenSource;
        CancellationToken token;
        Task t;

        public IntPtr Handle => throw new NotImplementedException();

        public string getAuthKey()
        {
            return KEY_AUTH;
        }

        public string GetUserId()
        {
            var user = Auth.DefaultInstance.CurrentUser;
            return user.Uid;
        }

        public bool IsUserSigned()
        {
            var user = Auth.DefaultInstance.CurrentUser;
            return user != null;
        }

        public async Task<bool> Logout()
        {
            NSError error;
            var signedOut = Auth.DefaultInstance.SignOut(out error);

            if (!signedOut)
            {
                return false;
            }

            return true;
        }

        //LOGIN USER/PASS
        public async Task<bool> SignIn(string email, string password)
        {
            Auth.DefaultInstance.SignIn(email, password, HandleAuthResultLoginHandler);
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            t = Task.Factory.StartNew(async () =>
            {
                await Task.Delay(4000);
            }, token).Unwrap();
            await t;

            return loginResult;
        }

        private void HandleAuthResultLoginHandler(User user, Foundation.NSError error)
        {
            if (error != null)
            {
                loginResult = false;
                hasLoginResult = true;
            }
            else
            {
                loginResult = true;
                hasLoginResult = true;
            }
            tokenSource.Cancel();
        }

        public async Task<bool> SignInWithGoogle(string tokenId)
        {
            String[] tokens = tokenId.Split(new string[] { "###" }, StringSplitOptions.None);
            var credential = GoogleAuthProvider.GetCredential(tokens[0], tokens[1]);
            Auth.DefaultInstance.SignIn(credential, HandleAuthResultHandlerGoogleSignin);
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            t = Task.Factory.StartNew(async () =>
            {
                await Task.Delay(4000);
            }, token).Unwrap();
            await t;

            return loginResult;
        }

        private void HandleAuthResultHandlerGoogleSignin(User user, NSError error)
        {
            if (error != null)
            {
                loginResult = false;
                hasLoginResult = true;
            }
            else
            {
                loginResult = true;
                hasLoginResult = true;
            }

            tokenSource.Cancel();
        }
        public void SignInWithGoogle()
        {
            XAuth = new OAuth2Authenticator(
            clientId: "745578531830-21hn7vjqkteplcblr9sgiai7ovjk1nil.apps.googleusercontent.com",
            clientSecret: "",
            scope: "profile",
            authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
            redirectUrl: new Uri("com.googleusercontent.apps.745578531830-21hn7vjqkteplcblr9sgiai7ovjk1nil:/oauth2redirect"),
            accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token"),
            isUsingNativeUI: true);

            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            XAuth.Completed += OnAuthenticationCompleted;

            XAuth.Error += OnAuthenticationFailed;

            var viewController = XAuth.GetUI();
            vc.PresentViewController(viewController, true, null);
        }

        private void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
        {
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            vc.DismissViewController(true, null);

            if (e.IsAuthenticated)
            {
                var access_token = e.Account.Properties["access_token"].ToString();
                var id_token = e.Account.Properties["id_token"].ToString();

                MessagingCenter.Send(FirebaseAuthService.KEY_AUTH, FirebaseAuthService.KEY_AUTH, id_token + "###" + access_token);
            }
            else
            {
                //Error
            }
        }

        private void OnAuthenticationFailed(object sender, AuthenticatorErrorEventArgs e)
        {
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            vc.DismissViewController(true, null);
        }

        public async Task<bool> SignUp(string email, string password)
        {
            Auth.DefaultInstance.CreateUser(email, password, HandleAuthResultHandlerSignUp);
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            t = Task.Factory.StartNew(async () =>
            {
                await Task.Delay(4000);
            }, token).Unwrap();
            await t;
            return signUpResult;
        }

        private void HandleAuthResultHandlerSignUp(User user, Foundation.NSError error)
        {
            if (error != null)
            {
                signUpResult = false;
                hasLoginResult = true;
            }
            else
            {
                signUpResult = true;
                hasLoginResult = true;
            }
            tokenSource.Cancel();
        }
    }
}
Y FirebaseDBService.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Firebase.Database;
using Foundation;
using SMove.iOS.Servicios;
using SMove.Services;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(FirebaseDBService))]
namespace SMove.iOS.Servicios
{
    public class FirebaseDBService : IFirebaseDBService
    {
        public static String KEY_MESSAGE = "items";

        private FirebaseAuthService authService = new FirebaseAuthService();
        DatabaseReference databaseReference;

        public void Connect()
        {
            databaseReference = Database.DefaultInstance.GetRootReference();
        }

        public void GetMessage()
        {
            var userId = authService.GetUserId();
            var messages = databaseReference.GetChild("items").GetChild(userId);

            nuint handleReference2 = messages.ObserveEvent(DataEventType.Value, (snapshot) =>
            {
                //var folderData = snapshot.GetValue();
                // Do magic with the folder data
                String message = "";
                if (snapshot.GetValue() != NSNull.Null)
                {
                    message = snapshot.GetValue().ToString();
                }
                MessagingCenter.Send(FirebaseDBService.KEY_MESSAGE, FirebaseDBService.KEY_MESSAGE, message);
            });
        }

        public void SetMessage(String message)
        {
            var userId = authService.GetUserId();
            var messages = databaseReference.GetChild("items").GetChild(userId).Reference;
            var key = messages.GetChildByAutoId().Key;
            messages.GetChild(key).SetValue((NSString)message);
        }

        public String GetMessageKey()
        {
            return KEY_MESSAGE;
        }

        public void DeleteItem(string key)
        {
            var userId = authService.GetUserId();
            var messages = databaseReference.GetChild("items").GetChild(userId).Reference;
            messages.GetChild(key).RemoveValue();
        }
    }
}
- Corregir el archivo readme.md.
Cambie la descripción del archivo readme del proyecto
Image

¿Qué voy a hacer?

- Verificar si se está conectando correctamente a la base de datos
- Planear Sprint #11

¿Qué problemas he tenido?

Ninguno durante este Sprint.

Velocidad inicial: 11
Velocidad final: 11

Burndown chart:
Image
Last edited by JuanG0617 on Mon Apr 22, 2019 4:18 pm, edited 1 time in total.
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Sprint #11

Post by JuanG0617 » Mon Apr 22, 2019 2:49 pm

Fecha de inicio: 22-04-2019
Fecha fin: 29-04-2019

Actividades

- Hacer publicas las página del API y del Backend. (2)
- Obtener Token de seguridad en la APP. (6)
- Conexión Base de Datos y controlador usuarios. (5)

¿Qué hice hasta hoy?

- Publiqué, mediante el Visual Studio las páginas web para el API y el Backend. Modifique el index del Backend

https://smoveapi.azurewebsites.net/
Image

https://smovebackend.azurewebsites.net/
Image

- Comencé utilizando Postman, una herramienta que me ayudará para obtener la información que necesito del API del proyecto
Image

Luego de hacer unas solicitudes a la página, esta me devolvía el token de seguridad de un usuario ya registrado con anterioridad
Image

Ahora, implementé en el Login del proyecto este método para así poder ingresar a la aplicación con un usuario registrado

LoginViewModel.cs

Code: Select all

namespace SMove.ViewModels
{
    using GalaSoft.MvvmLight.Command;
    using System;
    using System.ComponentModel;
    using System.Windows.Input;
    using Xamarin.Forms;
    using Views;
    using Services;

    public class LoginViewModel : BaseViewModel
    {
        #region Servicios
        private ApiService apiService;
        #endregion

        #region Atributos
        private string email;
        private string password;
        private bool isRunning;
        private bool isEnabled;
        #endregion

        #region Propiedades
        public string Email
        {
            get { return this.email; }
            set { SetValue(ref this.email, value); }
        }
        public string Password
        {
            get { return this.password; }
            set { SetValue(ref this.password, value); }
        }

        public bool IsRunning
        {
            get { return this.isRunning; }
            set { SetValue(ref this.isRunning, value); }
        }
        public bool IsRemembered { get; set; }
        public bool IsEnabled
        {
            get { return this.isEnabled; }
            set { SetValue(ref this.isEnabled, value); }
        }
        #endregion

        #region Constructores
        public LoginViewModel()
        {
            this.apiService = new ApiService();
            this.IsRemembered = true;
            this.IsEnabled = true;
        }
        #endregion
        #region Comandos
        public ICommand LoginCommand
        {
            get
            {
                return new RelayCommand(Login);
            }
        }

        public ICommand RegisterLCommand
        {
            get
            {
                return new RelayCommand(Register);
            }
        }

        private async void Login()
        {
            if (string.IsNullOrEmpty(this.Email))
            {
                await Application.Current.MainPage.DisplayAlert("Error", "Debe ingresar un correo.", "Aceptar");
                return;
            }
            if (string.IsNullOrEmpty(this.Password))
            {
                await Application.Current.MainPage.DisplayAlert("Error", "Debe ingresar una contraseña.", "Aceptar");
                return;
            }

            this.IsRunning = true;
            this.IsEnabled = false;

            var connection = await this.apiService.CheckConnection();
            if (!connection.IsSuccess)
            {
                this.IsRunning = false;
                this.IsEnabled = true;
                await Application.Current.MainPage.DisplayAlert("Error", connection.Message, "Aceptar");
                return;
            }

            var token = await this.apiService.GetToken("https://smoveapi.azurewebsites.net", this.Email, this.Password);

            if (token == null)
            {
                this.IsRunning = false;
                this.IsEnabled = true;
                await Application.Current.MainPage.DisplayAlert("Error", "Algo fallo, intente de nuevo", "Aceptar");
                return;
            }

            if (string.IsNullOrEmpty(token.AccessToken))
            {
                this.IsRunning = false;
                this.IsEnabled = true;
                await Application.Current.MainPage.DisplayAlert("Error", token.ErrorDescription, "Aceptar");
                this.Password = string.Empty;
                return;
            }

            var mainViewModel = MainViewModel.GetInstance();
            mainViewModel.Token = token;

            Application.Current.MainPage = new MasterPage();

            this.IsRunning = false;
            this.IsEnabled = true;

            this.Email = string.Empty;
            this.Password = string.Empty;

            

        }

        private async void Register()
        {
            MainViewModel.GetInstance().Register = new RegisterViewModel();
            await Application.Current.MainPage.Navigation.PushAsync(new RegisterPage());
        }
        #endregion
    }
}
- Comencé a utilizar el Domain del proyecto, de esta manera puedo conectar la base de datos con la aplicación y así poder realizar el registro de usuarios a través de esta

User.cs

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMove.Domain
{
    using Newtonsoft.Json;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;

    public class User
    {
        [Key]
        public int UserId { get; set; }

        [Display(Name = "Nombre")]
        [Required(ErrorMessage = "El campo {0} es necesario.")]
        [MaxLength(50, ErrorMessage = "El campo {0} solo puede contener un maximo de {1} caracteres.")]
        public string FirstName { get; set; }

        [Display(Name = "Apellidos")]
        [Required(ErrorMessage = "El campo {0} es necesario.")]
        [MaxLength(50, ErrorMessage = "El campo {0} solo puede contener un maximo de {1} caracteres.")]
        public string LastName { get; set; }

        [Required(ErrorMessage = "El campo {0} es necesario.")]
        [MaxLength(100, ErrorMessage = "El campo {0} solo puede contener un maximo de {1} caracteres.")]
        [Index("User_Email_Index", IsUnique = true)]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        [MaxLength(20, ErrorMessage = "El campo {0} solo puede contener un maximo de {1} caracteres.")]
        [DataType(DataType.PhoneNumber)]
        public string Telephone { get; set; }

        [Display(Name = "Image")]
        public string ImagePath { get; set; }

        [Display(Name = "Image")]
        public string ImageFullPath
        {
            get
            {
                if (string.IsNullOrEmpty(ImagePath))
                {
                    return "noimage";
                }

                return string.Format(
                    "https://smoveapi.azurewebsites.net/{0}",
                    ImagePath.Substring(1));
            }
        }

        [Display(Name = "User")]
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", this.FirstName, this.LastName);
            }
        }
    }
}
DataContext.cs

Code: Select all

using System.Data.Entity;

namespace SMove.Domain
{
    public class DataContext : DbContext
    {
        public DataContext() : base("DefaultConnection")
        {
        }
    }
}
Response.cs

Code: Select all

namespace SMove.Domain
{
    public class Response
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public object Result { get; set; }
    }
}
Con esto ya puedo manejar los usuarios desde la página web del Backend
Image

¿Qué voy a hacer?

- Planear Sprint #12
- Seguir con el registro de usuario a través de la aplicación

¿Qué problemas he tenido?
Habían errores y poca información de como conectar y utlizar Firebase junta a Xamarin Forms, se optó por volver a utilizar Azure junto a una base de datos SQL

Velocidad inicial: 13
Velocidad final: 13

Burndown chart
Image
Last edited by JuanG0617 on Wed Apr 24, 2019 10:28 pm, edited 1 time in total.
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Actividad - Día de la tierra

Post by JuanG0617 » Mon Apr 22, 2019 8:58 pm

Diagrama 1
Image

Diagrama 2
Image

Diagrama 3
Image

El Code Review lo realicé al proyecto Danceplan de la compañera Claudia
Last edited by JuanG0617 on Mon May 20, 2019 11:24 pm, edited 1 time in total.
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Sprint #12

Post by JuanG0617 » Mon May 06, 2019 7:53 am

Fecha de inicio: 29-04-2019
Fecha fin: 6-05-2019

Actividades

- Poner la aplicación con varios idiomas. (5)
- Mejorar el ingreso de nuevos usuarios. (5)

¿Qué hice hasta hoy?

- Poner la aplicación con varios idiomas. (5)
Se pondrá el idioma Español como predeterminado para la aplicación, un segundo idioma será el inglés. Para esto se creo la carpeta de Resources y la de Helpers.

La carpeta de helpers sera la que nos ayude a traducir las diferentes lineas de texto que vamos a usar:

Languages.cs

Code: Select all

namespace SMove.Helpers
{
    using Xamarin.Forms;
    using Interfaces;
    using Resources;

    public static class Languages
    {
        static Languages()
        {
            var ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo();
            Resource.Culture = ci;
            DependencyService.Get<ILocalize>().SetLocale(ci);
        }

        public static string Aceptar
        {
            get { return Resource.Aceptar; }
        }

        public static string Error
        {
            get { return Resource.Error; }
        }

        public static string ValidacionCorreo
        {
            get { return Resource.ValidacionCorreo; }
        }

        public static string ValidacionContra
        {
            get { return Resource.ValidacionContra; }
        }

        public static string Fallo
        {
            get { return Resource.Fallo; }
        }
    }
}
PlatformCulture.cs

Code: Select all

namespace SMove.Helpers
{
    using System;

    public class PlatformCulture
    {
        public PlatformCulture(string platformCultureString)
        {
            if (string.IsNullOrEmpty(platformCultureString))
            {
                throw new ArgumentException("Expected culture identifier", "platformCultureString"); // in C# 6 use nameof(platformCultureString)
            }

            PlatformString = platformCultureString.Replace("_", "-"); // .NET expects dash, not underscore
            var dashIndex = PlatformString.IndexOf("-", StringComparison.Ordinal);
            if (dashIndex > 0)
            {
                var parts = PlatformString.Split('-');
                LanguageCode = parts[0];
                LocaleCode = parts[1];
            }
            else
            {
                LanguageCode = PlatformString;
                LocaleCode = "";
            }
        }

        public string PlatformString { get; private set; }
        public string LanguageCode { get; private set; }
        public string LocaleCode { get; private set; }

        public override string ToString()
        {
            return PlatformString;
        }
    }
}
Ahora, en la carpeta de Resources tenemos los siguientes archivos para cada idioma

Inglés
Image

Español
Image

Para el proyecto individual en iOS y en Andriod se debió agregar una clase que implementara una interfaz para poder conseguir el idioma del dispositivo actual.

Code: Select all

namespace SMove.Interfaces
{
    using System.Globalization;

    public interface ILocalize
    {
        CultureInfo GetCurrentCultureInfo();
        void SetLocale(CultureInfo ci);
    }
}

- Mejorar el ingreso de nuevos usuarios. (5)
Al hacer unos cambios en la proyecto de Domain, surgió un error con el EntityFramework, por falta de tiempo no se pudo corregir este bug.
Image

¿Qué voy a hacer?

- Agregar más elementos para los idiomas y un tercer idioma que puede ser el portugués o el francés.
- Arreglar el error con el proyecto Domain

¿Qué problemas tuve?

El proyecto Domain me generaba un error respecto al EntityFramework, y al parecer con referencias al proyecto principal.

Velocidad inicial: 10
Velocidad final: 5

Burndown chart:
Image
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Sprint 13

Post by JuanG0617 » Mon May 13, 2019 3:09 pm

Fecha de inicio: 6-05-2019
Fecha fin: 13-05-2019

Actividades

- Mejorar el ingreso de usuarios. (8)
- Conseguir computador Mac. (1)
- Pruebas en iPhone. (1)

¿Qué hice hasta hoy?

- Mejoré el registro de usuarios en el proyecto Backend, se busca permitir que los usuarios se pudieran registrar desde la app y ni desde la página web para esto cree una carpeta que se llama Helpers, esta carpeta contiene los siguientes archivos:

DBHelper.cs

Code: Select all

namespace SMove.Backend.Helpers
{
    using System;
    using System.Threading.Tasks;
    using Domain;
    using Models;

    public class DBHelper
    {
        public async static Task<Response> SaveChanges(LocalDataContext db)
        {
            try
            {
                await db.SaveChangesAsync();
                return new Response { IsSuccess = true, };
            }
            catch (Exception ex)
            {
                var response = new Response { IsSuccess = false, };
                if (ex.InnerException != null &&
                    ex.InnerException.InnerException != null &&
                    ex.InnerException.InnerException.Message.Contains("_Index"))
                {
                    response.Message = "There is a record with the same value";
                }
                else if (ex.InnerException != null &&
                    ex.InnerException.InnerException != null &&
                    ex.InnerException.InnerException.Message.Contains("REFERENCE"))
                {
                    response.Message = "The record can't be delete because it has related records";
                }
                else
                {
                    response.Message = ex.Message;
                }

                return response;
            }
        }
    }
}
MailHelper.cs

Code: Select all

namespace SMove.Backend.Helpers
{
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Mail;
    using System.Threading.Tasks;
    using System.Web.Configuration;

    public class MailHelper
    {
        public static async Task SendMail(string to, string subject, string body)
        {
            var message = new MailMessage();
            message.To.Add(new MailAddress(to));
            message.From = new MailAddress(WebConfigurationManager.AppSettings["AdminUser"]);
            message.Subject = subject;
            message.Body = body;
            message.IsBodyHtml = true;

            using (var smtp = new SmtpClient())
            {
                var credential = new NetworkCredential
                {
                    UserName = WebConfigurationManager.AppSettings["AdminUser"],
                    Password = WebConfigurationManager.AppSettings["AdminPassWord"]
                };

                smtp.Credentials = credential;
                smtp.Host = WebConfigurationManager.AppSettings["SMTPName"];
                smtp.Port = int.Parse(WebConfigurationManager.AppSettings["SMTPPort"]);
                smtp.EnableSsl = true;
                await smtp.SendMailAsync(message);
            }
        }

        public static async Task SendMail(List<string> mails, string subject, string body)
        {
            var message = new MailMessage();

            foreach (var to in mails)
            {
                message.To.Add(new MailAddress(to));
            }

            message.From = new MailAddress(WebConfigurationManager.AppSettings["AdminUser"]);
            message.Subject = subject;
            message.Body = body;
            message.IsBodyHtml = true;

            using (var smtp = new SmtpClient())
            {
                var credential = new NetworkCredential
                {
                    UserName = WebConfigurationManager.AppSettings["AdminUser"],
                    Password = WebConfigurationManager.AppSettings["AdminPassWord"]
                };

                smtp.Credentials = credential;
                smtp.Host = WebConfigurationManager.AppSettings["SMTPName"];
                smtp.Port = int.Parse(WebConfigurationManager.AppSettings["SMTPPort"]);
                smtp.EnableSsl = true;
                await smtp.SendMailAsync(message);
            }
        }
    }
}
UsersHelper.cs

Code: Select all

namespace SMove.Backend.Helpers
{
    using Models;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.EntityFramework;
    using System;
    using System.Threading.Tasks;
    using System.Web.Configuration;

    public class UsersHelper : IDisposable
    {
        private static ApplicationDbContext userContext = new ApplicationDbContext();
        private static LocalDataContext db = new LocalDataContext();

        public static bool DeleteUser(string userName, string roleName)
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));
            var userASP = userManager.FindByEmail(userName);
            if (userASP == null)
            {
                return false;
            }

            var response = userManager.RemoveFromRole(userASP.Id, roleName);
            return response.Succeeded;
        }

        public static bool UpdateUserName(string currentUserName, string newUserName)
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));
            var userASP = userManager.FindByEmail(currentUserName);
            if (userASP == null)
            {
                return false;
            }

            userASP.UserName = newUserName;
            userASP.Email = newUserName;
            var response = userManager.Update(userASP);
            return response.Succeeded;
        }

        public static void CheckRole(string roleName)
        {
            var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(userContext));

            // Check to see if Role Exists, if not create it
            if (!roleManager.RoleExists(roleName))
            {
                roleManager.Create(new IdentityRole(roleName));
            }
        }

        public static void CheckSuperUser()
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));
            var email = WebConfigurationManager.AppSettings["AdminUser"];
            var password = WebConfigurationManager.AppSettings["AdminPassWord"];
            var userASP = userManager.FindByName(email);
            if (userASP == null)
            {
                CreateUserASP(email, "Admin", password);
                return;
            }
        }

        public static void CreateUserASP(string email, string roleName)
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));
            var userASP = userManager.FindByEmail(email);
            if (userASP == null)
            {
                userASP = new ApplicationUser
                {
                    Email = email,
                    UserName = email,
                };

                userManager.Create(userASP, email);
            }

            userManager.AddToRole(userASP.Id, roleName);
        }

        public static void CreateUserASP(string email, string roleName, string password)
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));

            var userASP = new ApplicationUser
            {
                Email = email,
                UserName = email,
            };

            var result = userManager.Create(userASP, password);
            if (result.Succeeded)
            {
                userManager.AddToRole(userASP.Id, roleName);
            }
        }

        public static async Task PasswordRecovery(string email)
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(userContext));
            var userASP = userManager.FindByEmail(email);
            if (userASP == null)
            {
                return;
            }

            var random = new Random();
            var newPassword = string.Format("{0}", random.Next(100000, 999999));
            var response = await userManager.AddPasswordAsync(userASP.Id, newPassword);
            if (response.Succeeded)
            {
                var subject = "Lands App - Recuperación de contraseña";
                var body = string.Format(@"
                    <h1>Lands App - Recuperación de contraseña</h1>
                    <p>Su nueva contraseña es: <strong>{0}</strong></p>
                    <p>Por favor no olvide cambiarla por una de fácil recordación",
                    newPassword);

                await MailHelper.SendMail(email, subject, body);
            }
        }

        public void Dispose()
        {
            userContext.Dispose();
            db.Dispose();
        }
    }
}
Y por ultimo, FilesHelper.cs

Code: Select all

namespace SMove.Backend.Helpers
{
    using System.IO;
    using System.Web;

    public class FilesHelper
    {
        public static string UploadPhoto(HttpPostedFileBase file, string folder)
        {
            string path = string.Empty;
            string pic = string.Empty;

            if (file != null)
            {
                pic = Path.GetFileName(file.FileName);
                path = Path.Combine(HttpContext.Current.Server.MapPath(folder), pic);
                file.SaveAs(path);
            }

            return pic;
        }

        public static bool UploadPhoto(MemoryStream stream, string folder, string name)
        {
            try
            {
                stream.Position = 0;
                var path = Path.Combine(HttpContext.Current.Server.MapPath(folder), name);
                File.WriteAllBytes(path, stream.ToArray());
            }
            catch
            {
                return false;
            }

            return true;
        }
    }
}
También creé un usuario administrador para hacer que sea el único que pueda modificar usuarios y eliminarlos.

Luego modifique la página para que el usuario no se pueda registrar desde esta página si no desde la app, pueden iniciar sesión desde esta página

- Conseguí que me prestaran un MacBook

- Por falta de tiempo, y esperando a que se descargaran las aplicaciones, no hice las pruebas en iPhone

¿Qué voy a hacer?

- Planear Sprint #14
- Traducir la app a más idiomas

¿Qué problemas he tenido?
Las descargas de los programas tomaron mucho tiempo

Velocidad inicial: 10
Velocidad final: 9

Burndown chart:
Image
Last edited by JuanG0617 on Mon May 20, 2019 11:52 pm, edited 1 time in total.
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

User avatar
JuanG0617
Posts: 17
Joined: Mon Jan 28, 2019 3:22 pm
Location: Medellín, Colombia

Sprint #14

Post by JuanG0617 » Mon May 20, 2019 10:54 pm

Fecha de inicio: 13-05-2019
Fecha fin: 20-05-2019

Actividades

- Corregir los diagramas. (1)
- Actualizar la presentación. (3)
- Utilizar el local storage. (7)

¿Qué hice hasta hoy?

- Corregí los diagramas 2 y 3 como indicó el profesor

Diagrama 2
Image

Diagrama 3
Image

- Actualicé la presentación del proyecto con lo que se tenía hasta el sprint pasado


¿Qué voy a hacer?

- Planear Sprint #14
- Terminar las actividades faltantes

¿Qué problemas he tenido?

El computador tuvo un error tras una actualización de windows

Velocidad inicial: 11
Velocidad final: 4
Juan Pablo García Díez :D
Ing. Sistemas
5° Semestre

Post Reply