Saturday, November 7, 2020

Complete Flutter Project 2

 





import 'package:flutter/material.dart';

import 'package:intl/intl.dart';
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Personal Expenses',
theme: ThemeData(
primarySwatch: Colors.purple,
accentColor: Colors.amber,
// errorColor: Colors.red,
fontFamily: 'Quicksand',
textTheme: ThemeData.light().textTheme.copyWith(
title: TextStyle(
fontFamily: 'OpenSans',
fontWeight: FontWeight.bold,
fontSize: 18,
),
button: TextStyle(color: Colors.white),
),
appBarTheme: AppBarTheme(
textTheme: ThemeData.light().textTheme.copyWith(
title: TextStyle(
fontFamily: 'OpenSans',
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
)),
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
// String titleInput;
// String amountInput;
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
final List<Transaction> _userTransactions = [
// Transaction(
// id: 't1',
// title: 'New Shoes',
// amount: 69.99,
// date: DateTime.now(),
// ),
// Transaction(
// id: 't2',
// title: 'Weekly Groceries',
// amount: 16.53,
// date: DateTime.now(),
// ),
];

List<Transaction> get _recentTransactions {
return _userTransactions.where((tx) {
return tx.date.isAfter(
DateTime.now().subtract(
Duration(days: 7),
),
);
}).toList();
}

void _addNewTransaction(
String txTitle, double txAmount, DateTime chosenDate) {
final newTx = Transaction(
title: txTitle,
amount: txAmount,
date: chosenDate,
id: DateTime.now().toString(),
);

setState(() {
_userTransactions.add(newTx);
});
}

void _startAddNewTransaction(BuildContext ctx) {
showModalBottomSheet(
context: ctx,
builder: (_) {
return GestureDetector(
onTap: () {},
child: NewTransaction(_addNewTransaction),
behavior: HitTestBehavior.opaque,
);
},
);
}

void _deleteTransaction(String id) {
setState(() {
_userTransactions.removeWhere((tx) => tx.id == id);
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Personal Expenses',
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () => _startAddNewTransaction(context),
),
],
),
body: SingleChildScrollView(
child: Column(
// mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Chart(_recentTransactions),
TransactionList(_userTransactions, _deleteTransaction),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _startAddNewTransaction(context),
),
);
}
}

class Transaction {
final String id;
final String title;
final double amount;
final DateTime date;

Transaction({
@required this.id,
@required this.title,
@required this.amount,
@required this.date,
});
}

class Chart extends StatelessWidget {
final List<Transaction> recentTransactions;

Chart(this.recentTransactions);

List<Map<String, Object>> get groupedTransactionValues {
return List.generate(7, (index) {
final weekDay = DateTime.now().subtract(
Duration(days: index),
);
var totalSum = 0.0;

for (var i = 0; i < recentTransactions.length; i++) {
if (recentTransactions[i].date.day == weekDay.day &&
recentTransactions[i].date.month == weekDay.month &&
recentTransactions[i].date.year == weekDay.year) {
totalSum += recentTransactions[i].amount;
}
}

return {
'day': DateFormat.E().format(weekDay).substring(0, 1),
'amount': totalSum,
};
}).reversed.toList();
}

double get totalSpending {
return groupedTransactionValues.fold(0.0, (sum, item) {
return sum + item['amount'];
});
}

@override
Widget build(BuildContext context) {
return Card(
elevation: 6,
margin: EdgeInsets.all(20),
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: groupedTransactionValues.map((data) {
return Flexible(
fit: FlexFit.tight,
child: ChartBar(
data['day'],
data['amount'],
totalSpending == 0.0
? 0.0
: (data['amount'] as double) / totalSpending,
),
);
}).toList(),
),
),
);
}
}

class ChartBar extends StatelessWidget {
final String label;
final double spendingAmount;
final double spendingPctOfTotal;

ChartBar(this.label, this.spendingAmount, this.spendingPctOfTotal);

@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
height: 20,
child: FittedBox(
child: Text('\$${spendingAmount.toStringAsFixed(0)}'),
),
),
SizedBox(
height: 4,
),
Container(
height: 60,
width: 10,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1.0),
color: Color.fromRGBO(220, 220, 220, 1),
borderRadius: BorderRadius.circular(10),
),
),
FractionallySizedBox(
heightFactor: spendingPctOfTotal,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(10),
),
),
),
],
),
),
SizedBox(
height: 4,
),
Text(label),
],
);
}
}

class NewTransaction extends StatefulWidget {
final Function addTx;

NewTransaction(this.addTx);

@override
_NewTransactionState createState() => _NewTransactionState();
}

class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime _selectedDate;

void _submitData() {
if (_amountController.text.isEmpty) {
return;
}
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);

if (enteredTitle.isEmpty || enteredAmount <= 0 || _selectedDate == null) {
return;
}

widget.addTx(
enteredTitle,
enteredAmount,
_selectedDate,
);

Navigator.of(context).pop();
}

void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
print('...');
}

@override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData(),
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData(),
// onChanged: (val) => amountInput = val,
),
Container(
height: 70,
child: Row(
children: <Widget>[
Expanded(
child: Text(
_selectedDate == null
? 'No Date Chosen!'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate)}',
),
),
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text(
'Choose Date',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
onPressed: _presentDatePicker,
),
],
),
),
RaisedButton(
child: Text('Add Transaction'),
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button.color,
onPressed: _submitData,
),
],
),
),
);
}
}



class TransactionList extends StatelessWidget {
final List<Transaction> transactions;
final Function deleteTx;

TransactionList(this.transactions, this.deleteTx);

@override
Widget build(BuildContext context) {
return Container(
height: 450,
child: transactions.isEmpty
? Column(
children: <Widget>[
Text(
'No transactions added yet!',
style: Theme.of(context).textTheme.title,
),
SizedBox(
height: 20,
),
Container(
height: 200,
child: Image.asset(
'assets/images/waiting.png',
fit: BoxFit.cover,
)),
],
)
: ListView.builder(
itemBuilder: (ctx, index) {
return Card(
elevation: 5,
margin: EdgeInsets.symmetric(
vertical: 8,
horizontal: 5,
),
child: ListTile(
leading: CircleAvatar(
radius: 30,
child: Padding(
padding: EdgeInsets.all(6),
child: FittedBox(
child: Text('\$${transactions[index].amount}'),
),
),
),
title: Text(
transactions[index].title,
style: Theme.of(context).textTheme.title,
),
subtitle: Text(
DateFormat.yMMMd().format(transactions[index].date),
),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Theme.of(context).errorColor,
onPressed: () => deleteTx(transactions[index].id),
),
),
);
},
itemCount: transactions.length,
),
);
}
}

Wednesday, November 4, 2020

Setup Solr 9.5 on Ubuntu

Solr installation comands

sudo add-apt-repository ppa:linuxuprising/java

sudo apt-get update

apt-get install openjdk-11-jdk -y

wget https://dlcdn.apache.org/solr/solr/9.5.0/solr-9.5.0.tgz

tar xzf solr-9.5.0.tgz solr-9.5.0/bin/install_solr_service.sh --strip-components=2

sudo bash ./install_solr_service.sh solr-9.5.0.tgz


http://127.0.0.1:8983/solr/#/

Create New core to host data inside it
sudo su - solr -c "/opt/solr/bin/solr create -c core_catalog_metadata_v2 -n data_driven_schema_configs"

Define Core Schema
Add your Fields schema inside managed-schema file found in path
/var/solr/data/core_catalog_metadata_v2/conf


Sample of fields that we can add

  <field name="status_id" type="pint" docValues="true" indexed="true" stored="false" />
  <field name="description" type="text_general" multiValued="false" indexed="false" stored="true"/>
  <field name="Comments" type="text_ar" indexed="true" stored="false" omitNorms="true" />
  

Define new Datatype that support Arabic
  <field name="FullName" type="text_ar_sort" multiValued="false" docValues="true" indexed="true" stored="true"/>

  
  <fieldType name="text_ar_sort" class="solr.SortableTextField" sortMissingLast="true" docValues="true"  positionIncrementGap="100" multiValued="false">
    <analyzer type="index">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" words="lang/stopwords_ar.txt" ignoreCase="true"/>
  <filter class="solr.ArabicNormalizationFilterFactory"/>
      <filter class="solr.ArabicStemFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" words="lang/stopwords_ar.txt" ignoreCase="true"/>
  <filter class="solr.ArabicNormalizationFilterFactory"/>
      <filter class="solr.ArabicStemFilterFactory"/>
      <filter class="solr.SynonymGraphFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
  </fieldType>



Solr Security

by default solr will listen to local calls only, to allow access:
nano /etc/default/solr.in.sh

listen to 0.0.0.0





Restrict network access to specific hosts, by activate Ubuntu firewall

sudo ufw enable
sudo ufw default deny incoming
sudo ufw allow OpenSSH
reboot

Add white list IPs that can access Solr

for ip in 193.199.247.17 193.42.243.96 193.35.34.22 193.59.15.7 193.89.167.125 193.59.21.206 193.189.32.185; 
do   
sudo ufw allow proto tcp from $ip to any port 8983 comment "Allow access to port 8983 from $ip"; 
done

View Firewall Status
sudo ufw status verbose

Allow new IP to access Solr
sudo ufw allow proto tcp from 193.35.116.116 to any port 8983 comment "Allow access to port 8983 from 193.35.116.116"